{"id":51052389,"url":"https://github.com/zachcheung/agevault-go","last_synced_at":"2026-06-22T18:03:06.740Z","repository":{"id":353201865,"uuid":"1210961138","full_name":"zachcheung/agevault-go","owner":"zachcheung","description":"agevault is a simple utility for managing age-encrypted secrets with ease – rewritten in Go.","archived":false,"fork":false,"pushed_at":"2026-04-22T21:08:58.000Z","size":138,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-22T23:18:31.238Z","etag":null,"topics":["age","age-encryption","encryption","gitops","secrets-management","security-tools"],"latest_commit_sha":null,"homepage":"","language":"Go","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/zachcheung.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2026-04-14T23:54:53.000Z","updated_at":"2026-04-22T21:09:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/zachcheung/agevault-go","commit_stats":null,"previous_names":["zachcheung/agevault-go"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/zachcheung/agevault-go","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zachcheung%2Fagevault-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zachcheung%2Fagevault-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zachcheung%2Fagevault-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zachcheung%2Fagevault-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zachcheung","download_url":"https://codeload.github.com/zachcheung/agevault-go/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zachcheung%2Fagevault-go/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34659899,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-22T02:00:06.391Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["age","age-encryption","encryption","gitops","secrets-management","security-tools"],"created_at":"2026-06-22T18:03:05.693Z","updated_at":"2026-06-22T18:03:06.724Z","avatar_url":"https://github.com/zachcheung.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# agevault\n\n`agevault` is a simple utility for managing [age](https://github.com/FiloSottile/age)-encrypted secrets with ease — rewritten in Go.\n\n\u003e This is a Go port of [zachcheung/agevault](https://github.com/zachcheung/agevault).  \n\u003e It uses the [`filippo.io/age`](https://pkg.go.dev/filippo.io/age) library directly — no `age` binary required.\n\n---\n\n## 📦 Installation\n\n**Install script** (Linux / macOS):\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/zachcheung/agevault-go/main/install.sh | sh\n```\n\nInstall to a custom directory:\n\n```sh\ncurl -fsSL https://raw.githubusercontent.com/zachcheung/agevault-go/main/install.sh | INSTALL_DIR=~/.local/bin sh\n```\n\n**Go install:**\n\n```sh\ngo install github.com/zachcheung/agevault-go/cmd/agevault@latest\n```\n\n**Docker / container image** — copy the binary into your own image:\n\n```dockerfile\nCOPY --from=ghcr.io/zachcheung/agevault:latest /ko-app/agevault /usr/local/bin/agevault\n```\n\nOr pin to a specific version:\n\n```dockerfile\nCOPY --from=ghcr.io/zachcheung/agevault:0.7 /ko-app/agevault /usr/local/bin/agevault\n```\n\n---\n\n## 🧠 Shell Completion\n\n**Bash** — install globally:\n\n```sh\nagevault completion bash | sudo tee /usr/share/bash-completion/completions/agevault \u003e /dev/null\n```\n\nOr per-user (`~/.bashrc`):\n\n```sh\nsource \u003c(agevault completion bash)\n```\n\n**Zsh:**\n\n```sh\nmkdir -p ~/.zsh/completions\nagevault completion zsh \u003e ~/.zsh/completions/_agevault\n```\n\nThen add to `~/.zshrc`:\n\n```sh\nfpath=(~/.zsh/completions $fpath)\nautoload -Uz compinit \u0026\u0026 compinit\n```\n\n---\n\n## 🚀 Usage\n\nBy default, `agevault` expects an age recipients file named `.age.txt` in the same directory as the secret file. Override with `AGE_RECIPIENTS` or `AGE_RECIPIENTS_FILE`.\n\n| Command      | Description                                                                                                                     | Example                                             |\n| ------------ | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |\n| `encrypt`    | Encrypt file(s)                                                                                                                 | `agevault encrypt secrets`                          |\n|              | `--self` — encrypt using identity (secret key)                                                                                  | `agevault encrypt --self secrets`                   |\n| `decrypt`    | Decrypt `.age` file(s)                                                                                                          | `agevault decrypt secrets.age`                      |\n| `cat`        | Decrypt and print to stdout                                                                                                     | `agevault cat secrets.age`                          |\n| `reencrypt`  | Re-encrypt file(s) with updated recipients file                                                                                 | `agevault reencrypt secrets.age`                    |\n|              | `--all` — re-encrypt all Git-tracked `*.age` files                                                                              | `agevault reencrypt --all`                          |\n| `rotate`     | Re-encrypt file(s) with a new key, update recipients file                                                                       | `agevault rotate secrets.age`                       |\n|              | `--keep-old-key` — keep old key in recipients                                                                                   | `agevault rotate --keep-old-key secrets.age`        |\n|              | `--new-key \u003cfile\u003e` — path for new key (default: `./age.key`, or `./age.key.enc` with `--kms-out`)                               | `agevault rotate --new-key ./new.key secrets.age`   |\n|              | `--kms-out` — generate key in memory, write KMS ciphertext to `--new-key`                                                       | `agevault rotate --kms-out secrets.age`             |\n|              | `--pq` — upgrade to post-quantum hybrid ML-KEM-768+X25519 key (all recipients must be hybrid; auto-preserved if already hybrid) | `agevault rotate --pq secrets.age`                  |\n|              | `--all` — rotate all Git-tracked `*.age` files                                                                                  | `agevault rotate --all`                             |\n| `edit`       | Edit encrypted file(s) securely in `$EDITOR`                                                                                    | `agevault edit secrets.age`                         |\n| `run`        | Decrypt `.age` env file(s) into env and run a command                                                                           | `agevault run env.age -- npm start`                 |\n|              | `--env FILES` — load as environment variables                                                                                   | `agevault run --env secrets.env.age -- npm start`   |\n|              | `--decrypt FILES` — decrypt files without loading env                                                                           | `agevault run --decrypt cert.pem.age -- ./start.sh` |\n| `init`       | Generate a new age key pair at `AGE_SECRET_KEY_FILE` (default: `~/.age/age.key`); fails if file exists                          | `agevault init`                                     |\n|              | `--pq` — generate a post-quantum hybrid ML-KEM-768+X25519 key                                                                   | `agevault init --pq`                                |\n| `keygen`     | Generate a new age key pair (no `age-keygen` needed)                                                                            | `agevault keygen`                                   |\n|              | `-o \u003cfile\u003e` — write private key to file                                                                                         | `agevault keygen -o ~/.age/age.key`                 |\n|              | `--pq` — generate a post-quantum hybrid ML-KEM-768+X25519 key                                                                   | `agevault keygen --pq -o ~/.age/age.key`            |\n|              | `-y \u003cfile\u003e` — print the public key of an existing private key file                                                              | `agevault keygen -y ~/.age/age.key`                 |\n| `key-add`    | Fetch public key(s) from `AGE_KEY_SERVER`, append to recipients                                                                 | `agevault key-add alice`                            |\n| `key-get`    | Fetch and print a public key from `AGE_KEY_SERVER`                                                                              | `agevault key-get alice`                            |\n| `key-readd`  | Reset recipients file and re-add key(s)                                                                                         | `agevault key-readd alice bob`                      |\n| `completion` | Generate shell completion script                                                                                                | `agevault completion zsh`                           |\n| `git-setup`  | Configure Git integration for `agevault` diff viewing                                                                           | `agevault git-setup`                                |\n\nIn most cases, `agevault edit` handles encryption, decryption, and editing of secrets in one step.\n\n---\n\n## 📂 Example\n\n```console\n$ cd $(mktemp -d)\n$ agevault init\nGenerated new age key pair:\n\n  Private key: ~/.age/age.key\n  Public key:  ~/.age/age.pub\n\nPublic key: age1...\n$ cp ~/.age/age.pub .age.txt\n$ echo \"my secret\" \u003e secrets\n\n$ agevault encrypt secrets\n'secrets' is encrypted to 'secrets.age'.\n$ rm secrets\n\n# Encrypt using your own identity (no recipients file needed)\n$ agevault encrypt --self secrets\n'secrets' is encrypted to 'secrets.age'.\n\n$ agevault decrypt secrets.age\n'secrets.age' is decrypted to 'secrets'.\n\n$ cat secrets \u0026\u0026 rm secrets\nmy secret\n\n$ agevault cat secrets.age\nmy secret\n\n$ agevault edit secrets.age\n'secrets.age' is updated.\n\n$ agevault cat secrets.age\nmy new secret\n\n$ agevault keygen -o ./age.key\nPublic key: age1newkey...\n$ agevault keygen -y ./age.key \u003e\u003e .age.txt\n\n# Re-encrypt with the new recipient included\n$ agevault reencrypt secrets.age\n'secrets.age' is reencrypted.\n\n# Now decryption with the new key works\n$ AGE_SECRET_KEY_FILE=./age.key agevault cat secrets.age\nmy new secret\n```\n\n---\n\n## 🏃 Run Command Examples\n\n**Environment Mode (default — backwards compatible):**\n\n```console\n$ echo \"API_KEY=secret123\" \u003e .env\n$ agevault encrypt .env\n$ agevault run .env.age -- curl -H \"Authorization: Bearer $API_KEY\" api.example.com\n```\n\n**Explicit Environment Mode:**\n\n```console\n$ echo \"DB_PASSWORD=secret\" \u003e database.env\n$ agevault encrypt database.env\n$ agevault run --env database.env.age -- ./deploy.sh\n```\n\n**Decrypt-only Mode:**\n\n```console\n$ echo \"sensitive data\" \u003e secret.txt\n$ agevault encrypt secret.txt\n$ agevault run --decrypt secret.txt.age -- cat secret.txt\nsensitive data\n```\n\n**Combined Mode:**\n\n```console\n$ agevault run --env config.env.age --decrypt cert.pem.age -- docker run -v $(pwd):/data myapp\n```\n\n**Comma-separated Files:**\n\n```console\n$ agevault run --env \"app.env.age,db.env.age\" --decrypt \"cert.pem.age,key.pem.age\" -- ./start-server.sh\n```\n\n---\n\n## 🔐 Configuration\n\n| Variable                    | Description                                             | Default                                                              |\n| --------------------------- | ------------------------------------------------------- | -------------------------------------------------------------------- |\n| `AGE_SECRET_KEY`            | Inline private key string (takes precedence)            | (unset)                                                              |\n| `AGE_SECRET_KEY_FILE`       | Path to your age private key                            | `~/.age/age.key`                                                     |\n| `AGE_RECIPIENTS`            | Comma-separated list of recipients (takes precedence)   | (unset)                                                              |\n| `AGE_RECIPIENTS_FILE`       | Path to the recipients list                             | `.age.txt` in same directory as the encrypted file                   |\n| `AGE_KEY_SERVER`            | Base URL for remote public keys                         | (must be set to use key commands)                                    |\n| `AGE_PUBKEY_EXT`            | Extension for age public keys on the key server         | `pub`                                                                |\n| `AGE_KMS_PROVIDER`          | KMS provider: `aws` or `gcp` (required if both are set) | (auto-detected)                                                      |\n| `AGE_AWS_KMS_ENCRYPTED_KEY` | Base64 AWS KMS ciphertext of the age private key        | (unset)                                                              |\n| `AGE_GCP_KMS_ENCRYPTED_KEY` | Base64 GCP KMS ciphertext of the age private key        | (unset)                                                              |\n| `AWS_KMS_KEY_ID`            | AWS KMS key ID / ARN / alias used for decryption        | (inferred from ciphertext metadata); required for `rotate --kms-out` |\n| `AWS_REGION`                | AWS region                                              | falls back to `AWS_DEFAULT_REGION`, then SDK default                 |\n| `GCP_KMS_KEY_NAME`          | GCP KMS key resource name                               | (required when using GCP KMS)                                        |\n\n\u003e [!NOTE]\n\u003e `AGE_KEY_SERVER` **must be set** to use `key-add`, `key-get`, or `key-readd`.\n\u003e\n\u003e For best security, prefer `AGE_SECRET_KEY_FILE` over `AGE_SECRET_KEY`.\n\u003e\n\u003e KMS takes precedence over `AGE_SECRET_KEY` / `AGE_SECRET_KEY_FILE`. Provider is auto-detected from which `*_ENCRYPTED_KEY` var is set. If both are set, `AGE_KMS_PROVIDER` must be set to disambiguate.\n\n---\n\n## ☁️ AWS KMS Integration\n\nStoring a plaintext age private key in CI (e.g. GitLab CI, GitHub Actions) is a security risk — any job can `echo` it. Instead, encrypt the key with AWS KMS and store only the ciphertext. At runtime, `agevault` calls KMS to decrypt the key, using the IAM role attached to the runner.\n\n**Encrypt your age key with KMS:**\n\n```sh\naws kms encrypt \\\n  --key-id alias/\u003ckey-alias\u003e \\\n  --region \u003cregion\u003e \\\n  --plaintext fileb://~/.age/age.key \\\n  --query CiphertextBlob \\\n  --output text\n```\n\nStore the output as `AGE_AWS_KMS_ENCRYPTED_KEY` in your CI secrets.\n\n**CI configuration (GitLab CI example):**\n\n```yaml\nvariables:\n  AGE_AWS_KMS_ENCRYPTED_KEY: $AGE_AWS_KMS_ENCRYPTED_KEY  # set in CI secrets\n\ndeploy:\n  script:\n    - agevault run --env config.env.age -- ./deploy.sh\n```\n\nThe runner's IAM role must have `kms:Decrypt` permission on the KMS key. No plaintext key is ever stored in CI.\n\n\u003e [!NOTE]\n\u003e `AWS_KMS_KEY_ID` is optional — AWS KMS can infer the key from the ciphertext metadata.\n\n**Rotating the KMS-protected key in CI:**\n\n```sh\n# Requires kms:Encrypt permission. AWS_KMS_KEY_ID must be set.\nagevault rotate --kms-out secrets.age\n# → generates a new key in memory, writes KMS ciphertext to ./age.key.enc\n```\n\n---\n\n## ☁️ GCP KMS Integration\n\n**Encrypt your age key with GCP KMS:**\n\n```sh\ngcloud kms encrypt \\\n  --key \u003ckey\u003e \\\n  --keyring \u003ckeyring\u003e \\\n  --location \u003clocation\u003e \\\n  --project \u003cproject\u003e \\\n  --plaintext-file ~/.age/age.key \\\n  --ciphertext-file - | base64\n```\n\nStore the output as `AGE_GCP_KMS_ENCRYPTED_KEY` and set `GCP_KMS_KEY_NAME`:\n\n```sh\nexport AGE_GCP_KMS_ENCRYPTED_KEY=\u003cbase64-ciphertext\u003e\nexport GCP_KMS_KEY_NAME='projects/\u003cproject\u003e/locations/\u003clocation\u003e/keyRings/\u003ckeyring\u003e/cryptoKeys/\u003ckey\u003e'\n```\n\nThe service account attached to the runner must have `cloudkms.cryptoKeyVersions.useToDecrypt` permission on the key.\n\n**Rotating the KMS-protected key in CI:**\n\n```sh\n# Requires cloudkms.cryptoKeyVersions.useToEncrypt permission.\nagevault rotate --kms-out secrets.age\n# → generates a new key in memory, writes KMS ciphertext to ./age.key.enc\n```\n\n\u003e [!NOTE]\n\u003e For local development, run `gcloud auth application-default login` — the Go client libraries use ADC separately from `gcloud` CLI credentials.\n\n---\n\n## 🌐 Key Management\n\n```sh\nexport AGE_KEY_SERVER=\"https://keys.example.com\"\n```\n\nBy default, `agevault` fetches each key from `$AGE_KEY_SERVER/\u003cusername\u003e.pub`.\n\n---\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzachcheung%2Fagevault-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzachcheung%2Fagevault-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzachcheung%2Fagevault-go/lists"}