* feat(buildinfo): expose the gopass build version to internal packages Internal packages cannot import the ldflags-injected version variable from package main, and runtime/debug build info cannot serve as a substitute: its Main.Version is "(devel)" in release builds and the main module never appears in Deps, so debug.ModuleVersion only resolves dependency versions reliably. main stashes the version in the exported Version variable at startup; an empty value means unknown (test builds, library embedders). ModuleVersion() wraps the lookup: the stash is authoritative, the build-info module version is a fallback. The first consumer (the age agent hello version token) follows in the next commit. Assisted-by: Claude-Code:GLM-5.3 Signed-off-by: Zexin Yuan <git@yzx9.xyz> * feat(out): add Untrusted and Truncated display wrappers Strings echoed to the terminal from untrusted origins - concretely the age agent's hello payload, which comes from whatever process owns the agent socket, a third party the protocol explicitly welcomes - would reach fmt's %s verbatim: control characters are not filtered, so ESC/OSC sequences could drive the terminal, and a hostile payload can be arbitrarily large (the response line limit is 16 MiB). Add two orthogonal fmt.Stringer wrappers to internal/out so every sink (terminals, debug logs, anything else that formats) renders them safely with zero plumbing in the print functions: Untrusted strips C0/C1 control characters, neutralizing escape sequences into inert text; Truncated bounds the rendered length at a rune boundary, shaped after io.LimitedReader and composable over any Stringer. Untrusted accepts a bare string on purpose: stripping is only semantically safe on raw bytes - a legitimate Stringer may intentionally emit ANSI - so the types keep the two operations in their safe domains. Assisted-by: Claude-Code:GLM-5.3 Signed-off-by: Zexin Yuan <git@yzx9.xyz> * refactor(age): remove the dead Client.Remove Client.Remove had a client-side implementation since the agent's introduction, but the agent never implemented the remove command and no caller ever invoked the method. Delete it rather than keep the illusion of a supported command. Assisted-by: Claude-Code:GLM-5.3 Signed-off-by: Zexin Yuan <git@yzx9.xyz> * feat(age): add hello capability negotiation to the age agent Implements a stateless, voluntary hello exchange: the agent advertises its capability set, and pre-hello agents keep working unchanged. - agent: dispatch via a handler table of bound methods returned by an Agent method; dispatch and the advertised capability list derive from the same table, so they cannot drift apart. Adds the hello command (command tokens + maxline + diagnostic version tokens). The session and ssh-identity commands dispatch through the same table and are advertised like every other command. - client: Capabilities() performs the handshake; ANY error (connection failure or an ERR from a pre-hello agent) means legacy and callers keep the pre-hello behaviour - judged by the error's existence, never its text. Response reads are bounded by the agent's own line limit, duplicate tokens resolve first-wins, and Info() fetches status and capabilities over a single connection. - gopass age agent status: extracted to a testable method, now reports the agent version and capabilities (or a legacy note), with agent-supplied strings stripped of terminal control characters before display. - tryStartAgent: logs agent capabilities behind the existing debug.IsEnabled() gate so the hot path pays no round trip unless debugging. - version sourcing: the hello version token and the client hello argument read internal/buildinfo.ModuleVersion. - changelog: record the capability negotiation under Unreleased. Invariants frozen by tests: unknown commands yield a single-line ERR, change no state and do not close the connection. Assisted-by: Claude-Code:GLM-5.3 Signed-off-by: Zexin Yuan <git@yzx9.xyz> * docs(age): document the age agent protocol Adds a complete specification of the agent's line protocol as its own document (docs/backends/age-agent-protocol.md): transport and framing, socket location rules per platform, per-command syntax, responses and errors, the lock state model including auto-lock semantics, the error-handling contract (judge by ERR presence, never by text), the hello capability exchange with its legacy-detection rule, and an example session. The token grammar and the client version argument may still change. age.md keeps a short overview and links to the specification. Assisted-by: Claude-Code:GLM-5.3 Signed-off-by: Zexin Yuan <git@yzx9.xyz> --------- Signed-off-by: Zexin Yuan <git@yzx9.xyz>
Overview
The slightly more awesome standard UNIX password manager for teams.
Manage your credentials with ease. In a globally distributed team, on multiple devices or fully offline on an air-gapped machine.
- Works everywhere - The same user experience on Linux, macOS, *BSD or Windows
- Built for teams - Built from our experience working in distributed development teams
- Full autonomy - No network connectivity required, unless you want it
How Does It Work?
Gopass is a drop-in replacement for pass, the standard UNIX password manager. By default your credentials are encrypted with GPG and versioned in git. This can be customized easily. Other backends for encryption (e.g. age) and storage (e.g. fossil) are also available. The primary interface is the command line, making it an excellent choice for CLI fans, CI/CD systems or anything you can hook it up with. Gopass can also integrate with your browser so you can largely avoid the command line - if you want.
Installation
Necessary prerequisites for running gopass
gopass can operate without any dependencies but most users will use it with gpg and git.
An external editor is required to use gopass edit.
Installation through package managers
Homebrew (Linux/macOS)
brew install gopass
MacPorts (macOS)
sudo port install gopass
Debian (Ubuntu, Debian, Raspbian, ...)
Warning: Do not install the gopass package from the official repositories. That is a completely different project that has no relation to us.
curl https://packages.gopass.pw/repos/gopass/gopass-archive-keyring.gpg | sudo tee /usr/share/keyrings/gopass-archive-keyring.gpg >/dev/null
cat << EOF | sudo tee /etc/apt/sources.list.d/gopass.sources
Types: deb
URIs: https://packages.gopass.pw/repos/gopass
Suites: stable
Architectures: all amd64 arm64 armhf
Components: main
Signed-By: /usr/share/keyrings/gopass-archive-keyring.gpg
EOF
sudo apt update
sudo apt install gopass gopass-archive-keyring
Fedora / RedHat / CentOS
dnf install gopass
Note: You might need to run dnf copr enable daftaupe/gopass first.
Arch Linux
pacman -S gopass
Windows
# WinGet
winget install -e --source winget --id Git.Git
winget install -e --source winget --id GnuPG.Gpg4win
winget install -e --source winget --id gopass.gopass
# Chocolatey
choco install gpg4win
choco install gopass
# Alternatively
scoop install gopass
FreeBSD / OpenBSD
cd /usr/ports/security/gopass
make install
Alpine Linux
apk add gopass
Other installation options
Please see docs/setup.md for other options.
From Source
go install github.com/gopasspw/gopass@latest
Note: latest is not a stable release. We recommend to only use released versions.
Manual download
Download the latest release and add the binary to your PATH.
Quick start guide
Initialize a new gopass configuration:
gopass setup
__ _ _ _ _ _ ___ ___
/'_ '\ /'_'\ ( '_'\ /'_' )/',__)/',__)
( (_) |( (_) )| (_) )( (_| |\__, \\__, \
'\__ |'\___/'| ,__/''\__,_)(____/(____/
( )_) | | |
\___/' (_)
🌟 Welcome to gopass!
🌟 Initializing a new password store ...
🌟 Configuring your password store ...
🎮 Please select a private key for encrypting secrets:
[0] gpg - 0xFEEDBEEF - John Doe <john.doe@example.org>
Please enter the number of a key (0-12, [q]uit) (q to abort) [0]: 0
❓ Do you want to add a git remote? [y/N/q]: y
Configuring the git remote ...
Please enter the git remote for your shared store []: git@gitlab.example.org:john/passwords.git
✅ Configured
By default gopass setup will use gpg encryption and git storage. This will create a new password store in $HOME/.local/share/gopass/stores/root and a configuration in $HOME/.config/gopass/config using gpg encryption and git for versioned storage. Users can override these with e.g. --crypto=age to use age encryption instead or opt out of using a versioned store with --storage=fs.
An existing store can be cloned with e.g. gopass clone git@gitlab.example.org:john/passwords.git.
Create a new secret:
gopass create
List all existing secrets:
gopass ls
Copy an existing password to the clipboard:
gopass show -c foo
Remove an existing secret:
gopass rm foo
Other examples:
# Command structure
gopass [<command>] [options] [args]
# Shortcut for gopass show [<key>]
gopass [<key>]
# Enter the gopass REPL
gopass
# Find all entries matching the search string
gopass find github
# List your store
gopass ls
# List all mounts
gopass mounts
# List all recipients
gopass recipients
# Sync with all remotes
gopass sync
# Setup a new store
gopass setup
Screenshot
Support
Please ask on Slack.
Contributing
We welcome any contributions. Please see CONTRIBUTING.md for more information.
Credit & License
gopass is licensed under the terms of the MIT license. You can find the complete text in LICENSE.
Please refer to our Contributors page for a complete list of our contributors.

