AxKeyStore is a secure, open-source command-line interface (CLI) tool designed to manage your secrets, keys, and passwords. It leverages your own private GitHub repository as the secure storage backend, ensuring your data is accessible, versioned, and under your control. Data travels encrypted over the wire and is stored encrypted in the remote repository. No secrets are ever stored in plain text in the remote repository. Also, no secrets are ever stored in the local filesystem or on any other remote server.
AxKeyStore is an Open Source Project built by Appxiom Team Visit https://www.appxiom.com to know more about us. You will love our product if you are into software engineering!
MIT License
AxKeyStore is built on a Zero Trust architecture with a robust multi-layered encryption scheme:
-
Local Master Key (LMK): A 36-character random alphanumeric string generated uniquely for each profile and stored on your local machine.
- Purpose: Encrypts your sensitive local configuration, including your GitHub access token and the name of your private repository.
- Security: The LMK itself is encrypted with the user's Master Password using
Argon2idandXChaCha20-Poly1305.
-
Remote Master Key (RMK): A 36-character random alphanumeric string generated uniquely for your vault and stored on GitHub.
- Purpose: Encrypts the actual secrets (keys/passwords) stored in your repository.
- Security: The RMK is encrypted with the user's Master Password (via client-side encryption) before being uploaded to GitHub.
-
Three-Layer Encryption:
- Secrets are encrypted using the RMK.
- RMK is encrypted using your Master Password and stored on GitHub.
- Local Credentials (Token/Repo Name) are encrypted using the LMK, which is also secured by your Master Password.
-
Client-Side Encryption: All encryption happens purely on your machine. No plain-text secrets, master keys, or passwords ever touch the network or are stored unencrypted on disk.
-
Untrusted Storage: GitHub is treated as untrusted cloud storage. It only ever sees encrypted binary blobs.
-
Secure Algorithms: Uses modern, authenticated encryption standards (
XChaCha20-Poly1305) and robust key derivation (Argon2id).
- GitHub Storage: Utilizes a private repository on your GitHub account or an organization for free, reliable, and versioned cloud storage.
- Unified Auth: Authenticates securely using GitHub Apps and the Device Flow.
- Installation Management: Simple one-time installation to grant access to specific repositories.
- Simple CLI: Easy-to-use commands to store and retrieve your credentials.
- Category Organization: Organize your secrets in hierarchical categories (e.g.,
api/production/internal). - Multi-Profile Support: Manage multiple vaults with different logins, master passwords, and GitHub repositories.
To install on macOS or Linux, run:
curl -sSL https://raw.githubusercontent.com/basilgregory/axkeystore/main/install.sh | bashTo install a specific version on macOS or Linux, run:
curl -sSL https://raw.githubusercontent.com/basilgregory/axkeystore/main/install.sh | bash -s v0.1.6To install on Windows, run the following command in PowerShell:
powershell -c "irm https://raw.githubusercontent.com/basilgregory/axkeystore/main/install.ps1 | iex"To install a specific version on Windows, run:
powershell -c "irm https://raw.githubusercontent.com/basilgregory/axkeystore/main/install.ps1 | iex -s v0.1.6"The scripts will download the appropriate binary, move it to $HOME/.axkeystore/bin, and automatically configure your PATH.
-
Login: Authenticate with your GitHub account.
axkeystore login
Note: During your first login, you will be prompted to set a Master Password. This password is used to encrypt your sensitive GitHub access token locally on your machine.
GitHub App Installation: After logging in, the CLI will provide a link to install the app on your GitHub account or organization:
https://github.com/apps/<app-name>/installations/new. You must install the app to grant it access to your repositories. -
Initialize: Set up a repository for storage (if not already done).
# Use a repo in your account axkeystore init --repo my-secret-store # Or specify an organization/owner axkeystore init --repo my-org/my-secret-store
Note: If the repository already exists and has been initialized previously (e.g., on another machine), AxKeyStore will prompt for your Master Password to verify access. You must provide the correct password associated with that repository to proceed.
-
Store a Secret: Encrypt and upload a key/password.
axkeystore store --key "my-api-key" --value "super_secret_value"
Note: You must run
axkeystore initbefore storing or retrieving any keys. If the repository is not configured, you will be prompted to do so. You must enter your Master Password for every operation to unlock your local session and vault. -
Auto-Generate a Secret: If you don't provide a value, AxKeyStore will generate a secure random alphanumeric value (6-36 characters) for you.
axkeystore store --key "my-api-key"You'll see the generated value and be asked to confirm before storing:
Generated value: qOmH8qHQ3pnuASPrho662Mqd (Length: 24 characters) Do you want to use this generated value? (y/n): -
Retrieve a Secret: Download and decrypt a key.
axkeystore get "my-api-key" -
View Version History: List previous versions of a key (10 at a time).
axkeystore history "my-api-key"
This will show a table with the SHA, date, and commit message for each version.
-
Retrieve a Specific Version: Use the SHA from history to retrieve a previous value.
axkeystore get "my-api-key" --version <SHA>
-
Store with Category: Organize secrets in hierarchical categories.
axkeystore store --key "aws-key" --value "AKIAIOSFODNN7EXAMPLE" --category "cloud/aws/production"
Tip: You can also auto-generate values with categories:
axkeystore store --key "aws-key" --category "cloud/aws/production"
-
Retrieve from Category: Retrieve a secret from a specific category.
axkeystore get "aws-key" --category "cloud/aws/production"
-
Delete a Secret: Delete a stored key (with confirmation prompt).
axkeystore delete "my-api-key" -
Delete from Category: Delete a secret from a specific category.
axkeystore delete "aws-key" --category "cloud/aws/production"
-
Reset Master Password: Update your master password safely.
axkeystore reset-password
Note: This command is profile-aware; it only resets the password for the currently active profile (or the one specified via
--profile). The process safely re-encrypts both your Local Master Key and your Remote Master Key with the new password. It is transactional: it updates the remote key on GitHub first, and only on success does it update the local configuration. -
Manage Profiles: AxKeyStore supports multiple profiles, each with its own master password, GitHub repository, and token.
# List all profiles axkeystore profile list # Create a new profile axkeystore profile create "work" # Switch to a profile (makes it the default for all commands) axkeystore profile switch "work" # Switch back to the default root profile axkeystore profile switch # Show the currently active profile axkeystore profile current # Delete a profile (and all its local configuration) axkeystore profile delete "work" # Use a specific profile for a single command without switching axkeystore --profile "personal" get "my-key"
When you create a new profile, you are creating an isolated configuration. Follow this flow to set it up:
- Create and Switch:
axkeystore profile create workthenaxkeystore profile switch work. - Login:
axkeystore login(this sets the unique Master Password for this profile). - Initialize:
axkeystore init --repo <YOUR_REPO>to link it to your GitHub storage.
- Profile names can only contain alphanumeric characters, dashes (
-), and underscores (_). - If no profile is specified and no profile has been set as active, the CLI uses the default "root" configuration directory.
- Each profile has its own isolated master password and local configuration.
- Categories can be nested using
/separator (e.g.,api/production/internal) - Category segments can only contain alphanumeric characters, dashes (
-), and underscores (_) - Key names cannot contain path separators
- Categories are optional; keys can be stored without any category
- Language: Rust
- CLI Framework:
clap - Async Runtime:
tokio - Crypto:
argon2,chacha20poly1305,rand - Path Resolution:
directories
AxKeyStore stores its configuration in the user's standard config directory (e.g., ~/Library/Application Support/com.ax.axkeystore on macOS).
com.ax.axkeystore/
├── global.json # Stores the active profile name
├── github_token.json # Profile-specific encrypted token (via LMK)
├── config.json # Profile-specific LMK and Repo config
└── <profile_name>/ # Subdirectory for each named profile
├── github_token.json # Sub-profile specific encrypted token
└── config.json # Sub-profile specific LMK and Repo config
Note: The
config.jsonfile contains your Local Master Key, which is encrypted with your Master Password. All other sensitive local files (likegithub_token.json) are encrypted using that LMK.
During development, you can run AxKeyStore directly using cargo. Use -- to separate cargo arguments from the CLI arguments:
# Authenticate (and follow the installation link provided)
cargo run -- login
# Initialize your vault (supports owner/repo format)
cargo run -- init --repo axkeystore-storage
cargo run -- init --repo my-org/my-keystore
# Store a secret
cargo run -- store --key "api-token" --value "secret123"
# Get a secret
cargo run -- get "api-token"
# List version history
cargo run -- history "api-token"
# Delete a secret
cargo run -- delete "api-token"
# Working with categories
cargo run -- store --key "db-pass" --category "prod/database" --value "top_secret"
cargo run -- get "db-pass" --category "prod/database"
cargo run -- history "db-pass" --category "prod/database"
cargo run -- delete "db-pass" --category "prod/database"
# Managing Profiles
cargo run -- profile list
cargo run -- profile create work
cargo run -- profile switch work
cargo run -- profile current
cargo run -- profile switch
cargo run -- profile delete work
# Using a specific profile flag
cargo run -- --profile personal get "my-key"
# Reset Master Password
cargo run -- reset-passwordAxKeyStore includes a robust suite of unit and integration tests. You can run them using:
cargo testcrypto: Verified authenticated encryption (XChaCha20-Poly1305), tamper detection, and Argon2id key derivation.auth: Tests for GitHub Device Flow response parsing and secure local token persistence.config: Validates global and profile-specific configuration encryption, isolation, profile name rules, and Local Master Key (LMK) generation and persistence.storage: Useswiremockto simulate the GitHub API, testing repository initialization, version history retrieval, and hierarchical category validation in a profile-aware context.
Note: Tests that modify process-wide environment variables (like API URLs) are synchronized using an internal
Mutexto ensure stability when running in parallel.
The following diagrams illustrate the internal logic and interactions for each command.
Authenticates the user via GitHub Device OAuth and secures the resulting token locally.
sequenceDiagram
participant U as User
participant C as CLI
participant G as GitHub API
participant GC as Global Config
participant PC as Profile Config
participant CR as Crypto Engine
U->>C: axkeystore login [--profile P]
C->>GC: Resolve Effective Profile
GC-->>C: Profile P / Active Profile
C->>G: Request Device Code (POST /login/device/code)
G-->>C: user_code, verification_uri, device_code
C->>U: Display "Visit URI and enter code XXXX"
loop Polling
C->>G: Request Access Token (POST /login/oauth/access_token)
G-->>C: Access Token / pending
end
C->>PC: Check for existing LMK
alt LMK exists
C->>U: Enter Master Password
U-->>C: (Password input)
C->>CR: Decrypt(LMK, Password)
note right of C: Verifies password against existing LMK
else New Profile
C->>U: Set Master Password (for profile)
U-->>C: (Password input)
C->>CR: Generate 36-char Local Master Key (LMK)
C->>CR: Encrypt(LMK, Password)
C->>PC: Save P/config.json (Encrypted LMK)
end
C->>CR: Encrypt(Token, LMK)
C->>PC: Save P/github_token.json (Encrypted Token)
C-->>U: Logged in successfully for profile
Sets up the remote repository and the encrypted master key used for all secrets.
sequenceDiagram
participant U as User
participant C as CLI
participant G as GitHub API
participant GC as Global Config
participant PC as Profile Config
participant CR as Crypto Engine
U->>C: axkeystore init --repo MY_REPO [--profile P]
C->>GC: Resolve Effective Profile
C->>U: Enter Master Password
U-->>C: (Password input)
C->>PC: Load & Decrypt LMK (using Password)
C->>G: Check if MY_REPO exists
alt Repo does not exist
C->>G: Create Private Repo (POST /user/repos)
end
C->>G: Get .axkeystore/master_key.json
alt Remote Master Key (RMK) exists
C->>CR: Decrypt(RMK, Password)
note right of C: Verifies password against Remote Key.<br/>Aborts if incorrect.
else RMK not found
C->>CR: Generate 36-char RMK
C->>CR: Encrypt(RMK, Password)
C->>G: Upload Encrypted RMK
end
C->>CR: Encrypt("MY_REPO", LMK)
C->>PC: Save P/config.json (Update Encrypted Repo Name)
C-->>U: Initialized successfully
Encrypts and uploads a secret. Supports auto-generation of secure values.
sequenceDiagram
participant U as User
participant C as CLI
participant G as GitHub API
participant GC as Global Config
participant PC as Profile Config
participant CR as Crypto Engine
U->>C: axkeystore store --key KEY [--profile P]
C->>GC: Resolve Effective Profile
C->>U: Enter Master Password
U-->>C: (Password input)
C->>PC: Load & Decrypt LMK (using Password)
C->>PC: Decrypt Repo Name (using LMK)
C->>G: Fetch Encrypted RMK
C->>CR: Decrypt(RMK Blob, Password)
alt Value not provided
C->>CR: Generate Random Secret
C->>U: Ask "Confirm secret XXXXX?"
U-->>C: Yes
end
C->>CR: Encrypt(Secret, RMK)
CR-->>C: EncryptedBlob
C->>G: Upload keys/KEY.json (Encrypted)
C-->>U: Secret stored successfully
Retrieves and decrypts a secret. Supports fetching specific versions via commit SHA.
sequenceDiagram
participant U as User
participant C as CLI
participant G as GitHub API
participant GC as Global Config
participant PC as Profile Config
participant CR as Crypto Engine
U->>C: axkeystore get KEY [--profile P]
C->>GC: Resolve Effective Profile
C->>U: Enter Master Password
U-->>C: (Password input)
C->>PC: Load & Decrypt LMK (using Password)
C->>PC: Decrypt Repo Name (using LMK)
C->>G: Fetch Encrypted RMK
C->>CR: Decrypt(RMK Blob, Password)
alt Version provided
C->>G: Fetch keys/KEY.json?ref=SHA
else Current
C->>G: Fetch keys/KEY.json
end
C->>CR: Decrypt(Blob, RMK)
CR-->>C: Plaintext secret
C-->>U: secret_value
Lists the version history (commits) of a specific key path on GitHub.
sequenceDiagram
participant U as User
participant C as CLI
participant G as GitHub API
participant GC as Global Config
participant PC as Profile Config
U->>C: axkeystore history KEY [--profile P]
C->>GC: Resolve Effective Profile
C->>U: Enter Master Password
U-->>C: (Password input)
C->>PC: Load & Decrypt Repo Name (for P)
loop Pagination
C->>G: Fetch Commits for path 'keys/KEY.json' (page X)
G-->>C: List of SHAs, dates, messages
C->>U: Display Table (SHA | Date | Message)
C->>U: Ask "Show more versions?"
U-->>C: Yes/No
end
Removes a secret from the repository.
sequenceDiagram
participant U as User
participant C as CLI
participant G as GitHub API
participant GC as Global Config
participant PC as Profile Config
U->>C: axkeystore delete KEY [--profile P]
C->>GC: Resolve Effective Profile
C->>U: Enter Master Password
U-->>C: (Password input)
C->>PC: Load & Decrypt Repo Name (for P)
C->>G: Fetch keys/KEY.json (to get SHA)
C->>U: Ask "Confirm delete KEY?"
U-->>C: Yes
C->>G: Delete file (DELETE /contents/path) with SHA
C-->>U: Secret deleted
Manages the active profile and profile-specific data directories.
sequenceDiagram
participant U as User
participant C as CLI
participant GC as Global Config
participant FS as File System
alt profile switch [NAME]
U->>C: axkeystore profile switch [NAME]
C->>GC: Set active_profile = NAME (or None)
C->>GC: Save global.json
C-->>U: Switched to NAME / default root
else profile list
U->>C: axkeystore profile list
C->>FS: Scan config root for directories
FS-->>C: List of profile names
C->>GC: Get active_profile
C-->>U: Display Profile List (* active)
else profile delete NAME
U->>C: axkeystore profile delete NAME
C->>FS: Remove directory config/NAME/
alt NAME was active
C->>GC: Clear active_profile
end
C-->>U: Profile deleted
end
Transactional update of both Remote and Local Master Keys.
sequenceDiagram
participant U as User
participant C as CLI
participant G as GitHub API
participant PC as Profile Config
participant CR as Crypto Engine
U->>C: axkeystore reset-password
C->>U: Enter current Master Password
U-->>C: (Password input)
C->>PC: Load & Decrypt LMK (verify old password)
alt Repo configured
C->>PC: Decrypt Repo Name
C->>G: Fetch Encrypted RMK
C->>CR: Decrypt(RMK, old password)
end
C->>U: Enter New Master Password (twice)
U-->>C: (New Password)
alt RMK exists
C->>CR: Encrypt(RMK, new password)
C->>G: Upload Updated RMK
note right of G: If this fails, the process aborts.
end
C->>CR: Encrypt(LMK, new password)
C->>PC: Save P/config.json (Updated LMK)
C-->>U: Master password reset successfully
To use AxKeyStore as YOUR OWN application, you need to register a GitHub App to get a Client ID:
- Go to GitHub Developer Settings > GitHub Apps.
- Click New GitHub App.
- Fill in the details:
- GitHub App name: A UNIQUE NAME OF YOUR CHOICE
- Homepage URL:
https://www.appxiom.comor your site. - Permissions:
- Repository permissions > Contents: Read & write (required for storing/retriving keys)
- Repository permissions > Metadata: Read-only (required)
- Where can this GitHub App be installed?: "Any account" or "Only on this account"
- Enable Device Flow (scroll down to find this checkbox).
- Click Create GitHub App.
- Note your App Name (slug) and Client ID.
- Update your
.envfile:GITHUB_CLIENT_ID=your_client_id GITHUB_APP_NAME=your_app_slug
