dotsecenv

module
v0.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2026 License: Apache-2.0

README

dotsecenv: safe environment secrets

CI Release GitHub Action E2E Contrib E2E Hermetic E2E Publish Packages Homebrew install Shell plugins CI Publish Website

A complete Go CLI application for securely managing environment secrets with GPG-based encryption, multi-user support, and FIPS 186-5 compliant algorithm defaults.

Quick Start

Store your first secret
# After installation (see below)
dotsecenv init config
dotsecenv init vault -v ~/.local/share/dotsecenv/vault
dotsecenv login <YOUR GPG FINGERPRINT> # gpg --list-public-keys
echo "xyz" | dotsecenv secret store TEST_SECRET
# Subsequently, you can decrypt secrets
# as long as you hold the corresponding secret key in GPG agent
dotsecenv secret get TEST_SECRET # should output "xyz"

Encryption defaults. Vaults use AES-256-GCM symmetric encryption (RFC 9580 / NIST SP 800-38D) wrapped in GPG multi-recipient asymmetric encryption. FIPS-compliant defaults are applied out of the box, with no extra flags at init time. To enforce specific algorithms across a team (or narrow them further), drop a policy.d/*.yaml fragment with approved_algorithms; see example 04.

Common recipes

Short, self-contained how-tos for the workflows beyond the quickstart:

Installation

The universal install script (install.sh) is the fastest way to install dotsecenv on macOS and Linux. It auto-detects your platform, downloads the correct binary, verifies checksums and GPG signatures, and installs shell completions, man pages, the shell plugin, and the Terraform credentials helper automatically.

Usage
# Piped (simplest)
curl -fsSL https://get.dotsecenv.com/install.sh | bash

# Piped with CLI flags
curl -fsSL https://get.dotsecenv.com/install.sh | bash -s -- [OPTIONS]

# Piped with environment variables
VERSION=v1.2.3 curl -fsSL https://get.dotsecenv.com/install.sh | bash

# Downloaded and run locally
curl -fsSL https://get.dotsecenv.com/install.sh -o install.sh
chmod +x install.sh
./install.sh --version v1.2.3

The script works with either curl or wget.

Options

Every setting can be configured via a CLI flag or an environment variable. Precedence order: CLI flags > environment variables > defaults.

Setting CLI Flag Env Var Default Description
Version --version VERSION VERSION latest Version to install (e.g., v1.2.3)
Install directory --install-dir DIR INSTALL_DIR ~/.local/bin Binary install path
System-wide install --system SYSTEM_INSTALL 0 (no) Install to system paths (/usr/local/bin on macOS, /usr/local/bin on Linux) instead of user-level paths
Shell plugin --[no-]install-shell-plugin INSTALL_SHELL_PLUGIN 1 (yes) Install the shell plugin for zsh/bash/fish
Completions --[no-]install-completions INSTALL_COMPLETIONS 1 (yes) Install shell completions for bash, zsh, and fish
Man pages --[no-]install-man-pages INSTALL_MAN_PAGES 1 (yes) Install man pages
TF credentials helper --[no-]install-tf-credentials-helper INSTALL_TF_CREDENTIALS_HELPER 1 (yes) Install the Terraform credentials helper
Verification --[no-]verify VERIFY 1 (yes) Verify SHA-256 checksums and GPG signatures
Help -h, --help Show help and exit
What the Installer Does
  1. Detects your platform — OS (Linux or macOS) and architecture (x86_64 or arm64)
  2. Resolves the version — Queries the GitHub API for the latest release, or uses the version you specified
  3. Downloads the release archive — From GitHub Releases
  4. Verifies integrity — SHA-256 checksum verification and GPG signature verification (when gpg is available)
  5. Installs the binary — To ~/.local/bin by default; pass --system to install to /usr/local/bin instead (uses sudo when needed)
  6. Installs shell completions — For bash, zsh, and fish, placed under ~/.local/share/ by default (or system directories with --system)
  7. Installs man pages — To ~/.local/share/man/man1/ by default (or /usr/local/share/man/man1/ / /usr/share/man/man1/ with --system)
  8. Installs the shell plugin — Auto-detects your plugin manager (Oh My Zsh, Zinit, Antidote, Oh My Bash, Fisher, Oh My Fish) and installs accordingly. Falls back to cloning to ~/.local/share/dotsecenv/plugin with manual sourcing instructions.
  9. Installs the Terraform credentials helper — To ~/.terraform.d/plugins/ (skip with --no-install-tf-credentials-helper)
Examples

CI/CD: Install specific version, skip interactive components:

VERSION=v1.2.3 INSTALL_SHELL_PLUGIN=0 INSTALL_MAN_PAGES=0 \
  curl -fsSL https://get.dotsecenv.com/install.sh | bash

Custom install directory:

curl -fsSL https://get.dotsecenv.com/install.sh | bash -s -- \
  --install-dir /opt/dotsecenv/bin

System-wide install:

curl -fsSL https://get.dotsecenv.com/install.sh | bash -s -- --system

Minimal install (binary only):

curl -fsSL https://get.dotsecenv.com/install.sh | bash -s -- \
  --no-install-shell-plugin \
  --no-install-completions \
  --no-install-man-pages \
  --no-install-tf-credentials-helper

Download and inspect before running:

curl -fsSL https://get.dotsecenv.com/install.sh -o install.sh
less install.sh  # review the script
bash install.sh --version v1.2.3
Mise
mise use github:dotsecenv/dotsecenv
macOS (Homebrew)
brew tap dotsecenv/tap
brew install dotsecenv
Linux Package Managers

Package repositories for Debian/Ubuntu, RHEL/CentOS/Fedora, and Arch Linux are available at get.dotsecenv.com.

Binary Download

Download the latest release for your platform from the Releases page.

Linux Packages (.deb / .rpm / .archlinux)

Download the appropriate package from the Releases page and install it:

Debian/Ubuntu:

sudo dpkg -i dotsecenv_amd64.deb

RHEL/CentOS/Fedora:

sudo rpm -i dotsecenv_amd64.rpm

Arch Linux:

sudo pacman -U dotsecenv_amd64.pkg.tar.zst
Windows

NOTICE: dotsecenv on Windows is currently WIP. You can follow this issue for updates.

Download the .zip file for your architecture from the Releases page:

  • dotsecenv_vX.X.X_Windows_x86_64.zip for 64-bit Intel/AMD
  • dotsecenv_vX.X.X_Windows_arm64.zip for ARM64

Extract and add the binary location to your PATH.

GPG Requirement: Install Gpg4win for GPG support. If GPG is not in your PATH, dotsecenv init config will attempt to detect it automatically, or you can set gpg_program in your config file.

Build from Source
# Clone the repository
git clone https://github.com/dotsecenv/dotsecenv.git
cd dotsecenv

# Build using make
make build

# Binary will be at bin/dotsecenv
GitHub Action

Use the official GitHub Action to install dotsecenv in your CI/CD workflows:

- uses: dotsecenv/dotsecenv@v0

The action downloads the appropriate binary for your runner's architecture and verifies its integrity.

Inputs

Release binaries achieve SLSA Build Level 3 compliance with verified provenance attestations. Using build-from-source: true or verify-provenance: false bypasses these security guarantees and is generally NOT recommended.

Input Default Description
version latest Version to install (e.g., v1.2.3 or latest)
build-from-source false Build from source instead of downloading a release
verify-provenance true Verify GPG signatures, checksums, and attestations
Outputs
Output Description
version The version of dotsecenv that was installed
binary-path Full path to the installed binary
Examples

Basic usage (latest release):

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: dotsecenv/dotsecenv@v0
      - run: dotsecenv secret get DATABASE_URL

Pin to a specific version:

- uses: dotsecenv/dotsecenv@v0
  with:
    version: v0.0.1

Build from source:

- uses: dotsecenv/dotsecenv@v0
  with:
    build-from-source: true
Shell Completions

dotsecenv supports shell completions for Bash, Zsh, Fish, and PowerShell.

Bash

Requires the bash-completion package:

# macOS
brew install bash-completion@2

# Debian/Ubuntu
sudo apt install bash-completion

# RHEL/CentOS/Fedora
sudo dnf install bash-completion

# Arch
sudo pacman -S bash-completion

Add to ~/.bashrc or ~/.bash_profile:

# Load bash-completion (macOS with Homebrew)
[[ -r "$(brew --prefix)/etc/profile.d/bash_completion.sh" ]] && . "$(brew --prefix)/etc/profile.d/bash_completion.sh"

# dotsecenv completions
if command -v dotsecenv &> /dev/null; then
  eval "$(dotsecenv completion bash)"
fi
Zsh

Add to ~/.zshrc:

# dotsecenv completions
if command -v dotsecenv &> /dev/null; then
  eval "$(dotsecenv completion zsh)"
fi
Fish

Add to ~/.config/fish/config.fish:

# dotsecenv completions
if command -v dotsecenv &> /dev/null
  dotsecenv completion fish | source
end
PowerShell

Add to your PowerShell profile ($PROFILE):

# dotsecenv completions
if (Get-Command dotsecenv -ErrorAction SilentlyContinue) {
  dotsecenv completion powershell | Out-String | Invoke-Expression
}
Pre-installed Paths

If you installed via a package manager (Homebrew, deb, rpm, Arch), completions are pre-installed at these paths:

Shell Homebrew (macOS/Linux) Linux Packages (deb/rpm/Arch)
Bash $(brew --prefix)/etc/bash_completion.d/dotsecenv /usr/share/bash-completion/completions/dotsecenv
Zsh $(brew --prefix)/share/zsh/site-functions/_dotsecenv /usr/share/zsh/site-functions/_dotsecenv
Fish $(brew --prefix)/share/fish/vendor_completions.d/dotsecenv.fish /usr/share/fish/vendor_completions.d/dotsecenv.fish
Shell Plugins

Shell plugins that automatically load .env and .secenv files when entering directories are available for zsh, bash, and fish.

For example, given a /path/to/project/.secenv file, e.g.:

A_SECRET={dotsecenv}
ANOTHER_SECRET={dotsecenv/SOME_OTHER_KEY}
MY_NAMESPACED_SECRET={dotsecenv/my::SECRET}

The three keys will be available as environment variables, when cd-ing into /path/to/project/.

Install shell plugins

You can install zsh/bash/fish plugins with:

curl -fsSL https://raw.githubusercontent.com/dotsecenv/plugin/main/install.sh | bash

For plugin manager installation and additional details, see github.com/dotsecenv/plugin#installation.

Basic Usage
# Initialize configuration
## Create default config
dotsecenv init config
## Specify where to store the config
dotsecenv init config -c /path/to/config
## Initialize the config with a single vault
dotsecenv init config -v /path/to/vault
## Customize both the config and the vault location
dotsecenv init config -c ... -v ...
## Pin GPG to an absolute path (default: gpg.program=PATH, resolved at runtime)
dotsecenv init config --gpg-program /usr/local/bin/gpg

# Initialize a vault
## Interactive prompt, asking which vault to initialize
dotsecenv init vault
## Specify which vault to initialize
dotsecenv init vault -v /path/to/vault

# Login with your GPG fingerprint
dotsecenv login <FINGERPRINT>

# Store a secret (reads value from stdin)
echo "secret-value" | dotsecenv secret store MY_SECRET

# Retrieve a secret
dotsecenv secret get MY_SECRET
dotsecenv secret get MY_SECRET --all   # All values across all vaults
dotsecenv secret get MY_SECRET --last  # Most recent value across all vaults
dotsecenv secret get MY_SECRET --json  # Output as JSON

# Share a secret with another identity (auto-adds identity if needed)
dotsecenv secret share MY_SECRET <TARGET_FINGERPRINT>

# Revoke access to a secret
dotsecenv secret revoke MY_SECRET <TARGET_FINGERPRINT>

# Describe vaults with identities and secrets
dotsecenv vault describe
dotsecenv vault describe --json

# Run health checks on vaults and environment
dotsecenv vault doctor
dotsecenv vault doctor --json

# Validate vault and config
dotsecenv validate
dotsecenv validate --fix  # Attempt to fix issues

Command Reference

Global Flags
Flag Short Description
--config -c Path to config file
--vault -v Path to vault file or vault index (1-based)
--silent -s Silent mode (suppress warnings)
Commands
Command Description
init config [--gpg-program PATH] Initialize configuration file
init vault Initialize vault file(s)
login FINGERPRINT Initialize user identity
secret store SECRET Store an encrypted secret (reads from stdin)
secret get SECRET [--all|--last|--json] Retrieve a secret value
secret share SECRET FINGERPRINT [--all] Share a secret with another identity
secret revoke SECRET FINGERPRINT [--all] Revoke access to a secret
vault describe [--json] Describe vaults with identities and secrets
vault doctor [--json] Run health checks and fix issues
validate [--fix] Validate vault and config integrity
version Show version information
completion Generate shell completion scripts

Features

  • Explicit Initialization: Safe bootstrapping of configuration and vaults
  • Encrypted at Rest: All secrets are encrypted using AES-256-GCM (RFC 9580)
  • Multi-User Support: Secrets can be encrypted for multiple identities using GPG multi-recipient encryption
  • Portable Vault: The vault file can be safely committed to git and shared between machines
  • Secret Sharing: Share and revoke access to secrets with other team members
  • Append-Only Design: Cryptographic history is preserved for audit trails
  • GPG Agent Integration: Leverages gpg-agent for secure key management
  • XDG Compliance: Respects XDG Base Directory Specification for configuration files
  • JSON Output: Machine-readable output format for scripting

Claude Code

Install the dotsecenv plugin for Claude Code:

/plugin marketplace add dotsecenv/dotsecenv
/plugin install dotsecenv@dotsecenv

See the Claude Code guide for details.

Configuration

Config File Resolution

The configuration file location is determined in the following order of precedence:

  1. -c flag (highest priority): Explicitly specify a config file path
  2. DOTSECENV_CONFIG environment variable: Override the default location
  3. XDG default: $XDG_CONFIG_HOME/dotsecenv/config (typically ~/.config/dotsecenv/config)

When both -c and DOTSECENV_CONFIG are specified, the -c flag takes precedence and a warning is printed to stderr (unless -s silent mode is enabled):

warning: DOTSECENV_CONFIG environment variable ignored because -c flag was specified
Config File Format

Example config:

# Default configuration uses FIPS 186-5 compliant algorithm minimums
approved_algorithms:
  - algo: ECC
    curves:
      - P-384
      - P-521
    min_bits: 384
  - algo: EdDSA
    curves:
      - Ed25519
      - Ed448
    min_bits: 255
  - algo: RSA
    min_bits: 2048
vault:
  - /path/to/vault1
gpg:
  program: gpg # Path to GPG executable
GPG Configuration

The gpg.program option specifies the path to the GPG executable.

Resolution order:

  1. Explicit configuration: If gpg.program is set, it must be an absolute path to an existing, executable program
  2. PATH inference: If gpg.program is not set (or empty), dotsecenv will look up gpg from your system PATH and print a warning to stderr

Examples:

# Explicit path (recommended for production)
gpg:
  program: /usr/bin/gpg

# Not specified - will infer from PATH with a warning
gpg:
  program: ""

# Windows with Gpg4win
gpg:
  program: "C:\\Program Files (x86)\\GnuPG\\bin\\gpg.exe"

Automatic detection: When running dotsecenv init config, dotsecenv will detect available GPG installations and set gpg.program to the detected absolute path. If multiple GPG installations are found, you'll be prompted to choose one.

When to use explicit paths:

  • When you have multiple GPG versions installed
  • When GPG is installed in a non-standard location
  • In CI/CD environments where PATH may vary

Policy Directory

System administrators can drop YAML policy fragments into /etc/dotsecenv/policy.d/ to constrain every user of the binary. User configs keep full local autonomy for fields that policy doesn't touch.

When the policy directory does not exist, no policy is enforced and the binary behaves exactly as before (today's behavior is preserved). When the directory exists, fragments are loaded in lexical filename order and merged into an effective policy that constrains every user.

Supported policy fields
# /etc/dotsecenv/policy.d/00-corp-baseline.yaml

# Allow-list fields: cross-fragment union; user vs policy is intersection.
approved_algorithms:
  - algo: ECC
    curves: [P-384, P-521]
    min_bits: 384
  - algo: EdDSA
    curves: [Ed25519, Ed448]
    min_bits: 255

approved_vault_paths:
  - /var/lib/dotsecenv/vault
  - ~/.local/share/dotsecenv/vault
  - ~/work/*/.dotsecenv/vault

# Scalar fields: cross-fragment last-wins; policy overrides user.
behavior:
  restrict_to_configured_vaults: true
  require_explicit_vault_upgrade: true

gpg:
  program: /usr/bin/gpg

Allow-list cross-fragment merge (approved_algorithms, approved_vault_paths): union of all fragments' entries. For approved_algorithms, same-algo entries collapse — curves union and min_bits takes the minimum (most-permissive reconciliation). For approved_vault_paths, deduped union of patterns.

Allow-list user vs policy (intersection): the user's approved_algorithms is intersected with the policy's; the user's cfg.Vault is filtered to entries matching at least one approved_vault_paths pattern. Policy is a ceiling; users can be stricter locally; users cannot exceed policy. Dropped entries emit stderr warnings explaining what changed (suppressed by -s).

Vault path matching uses path/filepath.Match (single-segment globs: *, ?, [abc]); ~ expands to the user's home. There is no ** recursive globbing — admins enumerate explicitly or use single-segment patterns. Explicit -v flags are subject to the same filter and are rejected (not silently dropped) when not allowed by policy.

Scalar cross-fragment merge (behavior.* per sub-field, gpg.program): last-fragment-to-set wins in lexical filename order, matching the Unix *.d drop-in convention used by sudoers.d, systemd unit drop-ins, nginx conf.d, etc. Naming: 00-base, 50-team, 99-overrides99- outranks 00- for scalars. When a later fragment overrides an earlier different value, dotsecenv emits a stderr warning citing both:

warning: policy conflict on gpg.program: overridden to "/usr/bin/gpg" by /etc/dotsecenv/policy.d/99-team.yaml; previous value "/opt/homebrew/bin/gpg" from /etc/dotsecenv/policy.d/00-corp-baseline.yaml

Same value across fragments emits no warning (it's redundant, not a conflict). For behavior.*, sub-fields are independent — fragment A setting behavior.X and fragment B setting behavior.Y do not conflict.

Scalar user vs policy (policy overrides user): if policy sets a scalar, that value replaces the user's. When the user had a different value, a stderr warning surfaces the override:

warning: policy overrides user gpg.program: user value "/opt/homebrew/bin/gpg" replaced by "/usr/bin/gpg" from /etc/dotsecenv/policy.d/00-corp-baseline.yaml

For behavior.*, "set" detection uses pointer nil-ness (*bool): an omitted field is "no opinion"; false is a real value distinct from omitted. For gpg.program, empty string is "no opinion"; the "PATH" sentinel is a real value distinct from empty.

Conflict warnings and user-override warnings both respect the -s silent flag.

Fail-closed on broken policy

Any error loading policy aborts startup. A partially-readable or malformed policy directory is indistinguishable from tampering, so the only safe behavior is to refuse to run. The error is classified by category and returned with the matching exit code (see policy validate table below); dotsecenv policy validate and dotsecenv policy list continue to work even when policy is broken (so admins can diagnose).

Forbidden keys in policy fragments

Hard-rejected at load with an explicit error citing the offending fragment:

  • login — identity is per-user (cryptographically bound to a private key)
  • vault — would erase user vaults; use approved_vault_paths instead
Permissions

The policy directory and each fragment must be:

  • Owned by root (uid 0)
  • Mode 0644 or stricter (no group/other write bits)

dotsecenv refuses to load any fragment that fails this check. Linux/macOS only — Windows uses ACLs and policy is not yet supported there.

Inspecting policy
# Print the effective policy with per-field origin attribution
dotsecenv policy list
dotsecenv policy list --json

# Validate fragment structure (useful in CI for ops repos shipping policy)
dotsecenv policy validate
dotsecenv policy validate --json   # error embedded in JSON object

policy validate exits with distinct codes per error category:

Code Meaning
0 No policy enforced or all fragments structurally valid
2 Malformed YAML or forbidden key
8 Insecure permissions or unreadable fragment
1 Empty allow-list field (omit the field instead)
Out of scope
  • Project-level .dotsecenv/policy.yaml
  • Remote/centralized policy distribution
  • Encrypted/signed policy fragments
  • Windows policy support (Linux/macOS only)
  • ** recursive glob support in approved_vault_paths
  • Per-command policy re-evaluation for stale logins

Vault File Format

Default vault location: $XDG_DATA_HOME/dotsecenv/vault

The vault uses a JSONL (JSON Lines) format for efficient append operations and indexed lookups. Each entry includes a hash and cryptographic signature to prevent against tampering.

Header (Line 1):

{
  "version": 1,
  "identities": [
    ["FINGERPRINT1", 2],
    ["FINGERPRINT2", 3]
  ],
  "secrets": { "SECRET_KEY": { "secret": 4, "values": [5, 6] } }
}

Identity Entry:

{
  "type": "identity",
  "data": {
    "added_at": "2024-11-09T12:00:00Z",
    "algorithm": "ECC",
    "algorithm_bits": 521,
    "curve": "P-521",
    "created_at": "2024-10-01T10:00:00Z",
    "fingerprint": "1E378219F90018AB2102B2131C238966B12A6F21",
    "hash": "sha256:...",
    "public_key": "base64...",
    "signed_by": "1E378219F90018AB2102B2131C238966B12A6F21",
    "signature": "base64...",
    "uid": "user@example.com"
  }
}

Secret Definition Entry:

{
  "type": "secret",
  "data": {
    "added_at": "2024-11-09T12:05:00Z",
    "hash": "sha256:...",
    "key": "DATABASE_URL",
    "signature": "base64...",
    "signed_by": "1E378219F90018AB2102B2131C238966B12A6F21"
  }
}

Secret Value Entry:

{
  "type": "value",
  "secret": "DATABASE_URL",
  "data": {
    "added_at": "2024-11-09T12:05:00Z",
    "available_to": ["1E378219F90018AB2102B2131C238966B12A6F21"],
    "hash": "sha256:...",
    "signature": "base64...",
    "signed_by": "1E378219F90018AB2102B2131C238966B12A6F21",
    "value": "base64-encrypted-value"
  }
}

Security Features

  • RFC 9580 OpenPGP compliance: Modern OpenPGP standard with mandatory AEAD encryption
  • AES-256-GCM symmetric encryption: NIST-approved authenticated encryption (SP 800-38D)
  • FIPS 186-5 digital signatures: RSA, ECDSA, and EdDSA signature schemes for vault entry authenticity and non-repudiation
  • FIPS 186-5 compliant defaults: Algorithm minimums meet the Digital Signature Standard requirements
  • FIPS 140-3 Crypto: Release binaries are built with Go's native FIPS 140-3 cryptographic module (GOFIPS140=v1.26.0) for NIST-validated cryptographic primitives on all platforms
  • Multi-recipient PGP encryption with hybrid cryptography
  • Hash-based integrity checking (SHA-256/SHA-512)
  • GPG agent integration for secure key management
  • Full secret encryption/decryption lifecycle
  • Validation logic with optional auto-fix
  • SLSA Build Level 3: Release binaries include verifiable provenance attestations generated via GitHub's attest-build-provenance action on hardened GitHub-hosted runners
  • Hermetic E2E Testing: Every pull request runs e2e tests in a network-isolated Linux namespace with eBPF verification via harden-runner, proving zero external network connections. See Security Model.

Exit Codes

Code Name Description
0 Success Operation completed successfully
1 General Error Unspecified error
2 Config Error Configuration file issue
3 Vault Error Vault file issue
4 GPG Error GPG operation failed
5 Auth Error Authentication failed
6 Validation Error Validation failed
7 Fingerprint Required No fingerprint configured
8 Access Denied Permission denied
9 Algorithm Not Allowed Algorithm not in allow-list

Environment Variables

Variable Description
DOTSECENV_CONFIG Override config file path
XDG_CONFIG_HOME Override config directory (defaults to: ~/.config)
XDG_DATA_HOME Override data directory (defaults to: ~/.local/share)

Known Limitations

Ed448 Keys (GnuPG v5 Format)

Ed448 keys generated by GnuPG 2.4+ use the OpenPGP v5 key format, which is not yet fully supported by the underlying cryptographic library (gopenpgp/go-crypto). This results in a parsing error when trying to add Ed448 identities:

failed to get public key: failed to parse public key: gopenpgp: error in reading key ring: openpgp: invalid data: first packet was not a public/private key

Workaround: Use Ed25519 keys instead, which are fully supported and provide equivalent security for most use cases. Ed25519 keys use the OpenPGP v4 format which has full library support.

Status: This limitation will be resolved when:

  • GnuPG adopts RFC 9580 v6 format for Ed448 keys, OR
  • go-crypto adds compatibility for GnuPG's v5 Ed448 format

Ed448 is included in the approved algorithms configuration to ensure readiness when support becomes available.

FAQ

How do I generate a GPG key?
# Generate a new GPG key and choose sensible defaults, i.e.:
# - (9) ECC (sign and encrypt)
# - (1) Curve 25519
# - Key expiration: 1y
gpg --full-generate-key
agent_genkey failed: No pinentry

If you are unable to generate a key due to this error, install pinentry:

# macOS
brew install pinentry-mac

# Linux
sudo apt-get install pinentry
sudo dnf install pinentry-tty
sudo yum install pinentry-tty
sudo pacman -S pinentry-tty
# etc.

In rare cases you may need to add a pinentry-program line to your ~/.gnupg/gpg-agent.conf and restart the gpg-agent (killall gpg-agent).

gpg: signing failed: Inappropriate ioctl for device

This error occurs when GPG cannot find the terminal for pinentry input. Add the following to your shell profile (~/.bashrc, ~/.zshrc):

export GPG_TTY=$(tty)

Or for fish shell, add the following to ~/.config/fish/config.fish:

set -gx GPG_TTY (tty)

Then restart your shell or run the command directly.

gpg: decryption failed

If you encounter this error, first try to define GPG_TTY (see above) before searching for other possible solutions.

$ dotsecenv secret get bla
failed to decrypt secret: failed to decrypt with gpg-agent: exit status 2
GPG error: gpg: decryption failed: No secret key

Development

Setup
# Install all development tools (lefthook, golangci-lint, syft, goreleaser)
make install-tools
Building
# Build with FIPS 140-3 crypto (no CGO required)
make build

The default make build uses Go's native FIPS 140-3 cryptographic module (GOFIPS140=v1.26.0), which provides NIST-validated cryptographic primitives on all platforms without requiring CGO. See go.dev/blog/fips140 for details.

Testing
# Run all tests
make test

# Run tests with race condition detection
make test-race

# Run end-to-end tests
make build e2e
Linting
# Run linting (installs golangci-lint if needed)
make lint
Releasing

Releases are triggered by pushing a signed semver tag. Following GitHub Actions conventions, a major version tag (e.g., v0) should also be maintained to allow users to pin to a major version.

The releasetools-cli simplifies this process:

rt git::release --major --sign --force --push v0.1.2

This creates both v0.1.2 and v0 tags pointing to the same commit, signs them, and pushes to the remote.

Security Considerations

What dotsecenv Protects Against
  • Accidental exposure of unencrypted secrets in version control
  • Secrets stored in plaintext on disk
  • Access to secrets by unauthorized users without GPG keys
  • Tampering with vault entries (signature verification)
  • Stealth network exfiltration of secrets during CI/CD (hermetic testing)
What dotsecenv DOES NOT Protect Against
  • Operating system-level access (root/admin can always read memory)
  • Compromised GPG private keys
  • Quantum computing attacks (future consideration)
  • Side-channel attacks
  • Physical memory dumps
  • Environment snooping
Recommendations
  1. Private Keys: Never commit GPG private keys to repositories
  2. Key Management: Use gpg-agent with passphrase protection
  3. Vault Files: Can be committed to git, technically safe in public repositories, but not recommended
  4. Multi-User Systems: Use strong user isolation and file permissions
  5. Monitoring: Audit all secret access in production environments
  6. Rotation: Periodically rotate encryption keys by updating and then re-encrypting secrets

License

Apache 2.0 License. See LICENSE file for details.

Acknowledgments

Directories

Path Synopsis
cmd
dotsecenv command
internal
cli
xdg
pkg
dotsecenv/policy
Package policy implements the trusted system policy directory at /etc/dotsecenv/policy.d/.
Package policy implements the trusted system policy directory at /etc/dotsecenv/policy.d/.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL