mirror of
https://github.com/gopasspw/gopass.git
synced 2026-10-08 17:49:55 +02:00
* 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>