Zexin Yuan a8505c6667 feat(age): add hello capability negotiation to the age agent (#3634)
* 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>
2026-10-07 20:17:55 +02:00
2026-08-30 15:55:58 +02:00
2026-09-22 20:15:26 +02:00
…
2026-10-03 22:38:23 +02:00
2026-09-22 20:15:26 +02:00
2026-09-22 20:15:26 +02:00

gopass Gopher by Vincent Leinweber, remixed from the Renée French original Gopher

Overview

OpenSSF Scorecard Build Status Lint Status Packaging Status MIT License Github All Releases Gopass Slack

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)

homebrew version

brew install gopass

MacPorts (macOS)

macports version

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

Fedora version

dnf install gopass

Note: You might need to run dnf copr enable daftaupe/gopass first.

Arch Linux

Arch version

pacman -S gopass

Windows

Scoop version

# 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

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.

S
Description
The slightly more awesome standard unix password manager for teams
Readme MIT
29 MiB
0 Stars 1 Watchers 0 Forks
Languages
Go 91.6%
Shell 7.1%
Roff 0.9%
Makefile 0.3%