Summary

  • RFC 9987 standardizes a client-driven SSH agent protocol. A sign request names a public key, supplies data and flags, and receives a signature or failure; the response does not carry a standard account of human intent, process identity, destination or business purpose.
  • The agent protocol has no authentication or transport protection of its own. Access to its endpoint is normally enough to ask for private-key operations, and forwarding that access creates transitive trust even though the private key bytes remain local.
  • A confirmation constraint, lifetime, agent lock and downstream SSH server decision are separate controls. Daniel Kade proposes a privacy-preserving signing-intent receipt that joins these verdicts without storing private keys, passphrases or sensitive signature input.

The signature can be valid while the authority story is incomplete

An operations team may be shown a reassuring result: the signing agent returned success, the signature verifies, and the private key was never exported. Each statement can be true while the team still cannot answer the governance question. Which process asked for the operation? Was it local, or did a remote host reach the agent through a forwarded channel? Which constraints were active at that moment? If a person confirmed, what safe context did the prompt reveal? Did the relying SSH server accept the key for the requested account, demand another factor, reject the request, or admit a session whose later command was denied?

RFC 9987, published in May 2026 as an IETF Proposed Standard, defines the protocol between a client and a component that holds or delegates access to private keys. The protocol is packetized, request-response and driven entirely by the client. An agent may load keys, remove them, enumerate the keys visible to a client, perform signature operations, lock itself and expose extensions. That specification turns a long-standing interface into a common protocol. It does not turn the agent into an institution that understands every purpose for which a signature will be used.

The distinction begins in the sign request itself. SSH_AGENTC_SIGN_REQUEST contains a public-key blob, a data string and flags. A successful reply contains SSH_AGENT_SIGN_RESPONSE and the resulting signature. The data may be an SSH authentication request, but the generic agent message does not declare that as its only possible meaning. It has no mandatory field for an initiating executable, a human principal, a target host, a remote account, a command, a ticket, an approval rule or an authorization result. Cryptographic correctness says that the selected key signed the supplied bytes. It cannot manufacture the missing context.

That is not a defect in the standard. Narrow interfaces can be more interoperable and easier to protect. The mistake occurs later, when a dashboard promotes “agent returned a signature” into “the user consented” or “the action was authorized.” Why BTW Media Exists argues for preserving the difference between a claim and what can actually be observed. Here the observable event is a key operation. Consent and authorization remain separate claims until their own evidence exists.

Key custody prevents extraction, not every unauthorized use

The security section of RFC 9987 is unusually direct. The agent protocol itself supplies neither client authentication nor transport security. On common systems, a local socket or named pipe carries the protocol, and the surrounding operating system is expected to restrict access to the owner and authorized delegates. The standard warns that merely reaching an agent is usually enough to invoke private-key operations.

This gives key custody two different meanings. The first is possession of the private bytes. An agent can reduce exposure by keeping the key inside a smaller, specially protected process or a hardware token. The second is the authority to make the key act. A hostile process may fail to extract the key yet still ask an accessible, unconstrained agent to sign. RFC 9987 describes that condition as stolen use and notes that an unconstrained agent can become an almost ideal oracle for side-channel observation. “Non-exportable” is therefore useful evidence about custody, not a universal verdict about legitimate use.

The same separation applies to hardware-backed keys. Loading a token-hosted identity into the agent commonly delegates future private-key operations to the token; removing it from the agent should not delete it from the token. Agent inventory and token inventory are different states. An emergency record that says “identity removed” is incomplete unless it names the layer from which the identity disappeared and the remaining path by which the token could be invoked again.

The Policy Mirror offers the right discipline: policy should reflect the real distribution of control. The agent controls whether its interface will perform an operation. The operating system controls who can reach the endpoint. A token can control key material and perhaps local presence. The remote server controls whether a key is an acceptable authenticator. A service or shell policy controls what an authenticated session may do. None of those actors acquires the others' decision merely because all decisions eventually touch one signature.

Constraints are state, not decoration

RFC 9987 defines constrained key loading. A lifetime constraint asks the agent to delete a key after a duration. A confirmation constraint asks for explicit user confirmation before each private-key operation. Named extensions can introduce other restrictions, and an agent that does not understand a requested constraint must reject the constrained key rather than silently ignore the limit.

Those rules create an important evidence boundary. A key fingerprint alone does not reveal its current authority. If the same key is added again, the standard says the new request should replace its previous constraints, or the agent may refuse the load. The question is therefore not merely “was this key constrained?” It is “which constraint set was active for this client, under which load or replacement epoch, when this operation occurred?” A screenshot taken yesterday and an inventory taken after an incident cannot answer that time-specific question.

Confirmation deserves equally careful treatment. The standard gives a confirmation dialog as an example; it does not prescribe a universal display contract. A click proves that an interaction returned an affirmative result. It proves informed consent only when the system can also show that the person saw context that accurately represented the bytes, the destination and the consequence. Raw data may be too sensitive or too opaque to display, but a generic message such as “allow key use?” is too thin to carry an account-level or command-level decision.

The operational interface must present a safe, intelligible summary and bind it to the exact operation.

Lock state is another independent verdict. When locked, the agent must suspend at least private-key signature operations until the correct passphrase unlocks it. A successful unlock establishes an agent state transition. It does not identify the later requester, approve a destination, or make every loaded key appropriate for every account. Treating unlock as blanket consent converts a temporary capability-enablement event into authority it was never designed to bear.

Forwarding delegates use across a trust boundary

Agent forwarding is attractive because a remote system can use a local key without receiving the private key material. That is a real security property. It is also a delegation. RFC 9987 calls forwarding a transitive trust relationship, recommends that implementations not enable it by default, and tells users not to forward an agent to hosts they do not fully trust. An attacker on the remote host may be able to ask the forwarded agent to use a key even though the key remains on the client.

The channel structure limits retrospective attribution. RFC 9987 states that agent-connect carries no identifier distinguishing the session channel that originated the connection request. An SSH connection can carry multiple sessions, and a client-side agent may see concurrent forwarded connections, yet the base channel cannot by itself name the exact originating session. “It came over this SSH transport” is not necessarily “this shell, process or person requested it.”

This is where Running Code Primary matters operationally. A policy against unnecessary forwarding is worthwhile, but evidence must come from the running boundaries: socket ownership, connection state, forwarding choice, active constraints, request timing and the later verifier result. A configuration declaration without those observations cannot prove which path a particular signature followed.

The SSH server owns the authentication verdict

The downstream SSH protocol supplies context the generic agent lacks. RFC 4252 defines public-key user authentication. Its signature covers the SSH session identifier and request fields that include user name, service, method, algorithm and public key. The server must decide whether the key is a valid authenticator for that user and verify the signature. Even then, it may require additional authentication. Final authentication is marked by the server's SSH_MSG_USERAUTH_SUCCESS, not by the agent's earlier signature response.

RFC 4253 establishes the protected transport and the session identifier. RFC 4254 governs channels and services after authentication. RFC 4251 supplies the overall architecture. This layered arrangement prevents one signature from being the whole story: key use, server authentication and the authority to open or operate a channel remain different decisions.

Algorithm records are also narrower than authority. RFC 8332 specifies RSA SHA-2 signature methods, while RFC 8709 specifies Ed25519 and Ed448 for SSH. RFC 8308 provides extension negotiation. The IANA SSH parameters registry records interoperable identifiers, including those created by RFC 9987. These sources tell implementers how peers name and process mechanisms. They do not say that a registered method was deployed, that a specific endpoint exposed an agent, or that a human approved a particular use.

A six-stage signing-intent receipt

An accountable design needs a receipt that follows the authority chain without turning the audit system into a secret store. The first stage records requester admission: whether the path was local or forwarded, the connection or endpoint evidence available, the policy epoch and a request identifier. Where peer identity is available from the surrounding system, record a stable, minimized reference rather than an unbounded process dump.

The second stage records key state: a public-key fingerprint, visibility scope, load or replacement epoch, active lifetime, confirmation and extension constraints, token delegation where applicable, and lock state. It must not contain the private key, token PIN or unlock passphrase.

The third stage records the operation. Store the length and a collision-resistant digest of the exact data submitted to the agent, together with the requested key algorithm and signature flags. Sensitive cleartext should remain in the system that already needs it. A digest creates a join point without copying account names, commands or other material into a broader telemetry plane.

The fourth stage records confirmation. Was it required? What safe description of the destination, account, protocol and consequence was displayed? Which trusted interaction surface returned the decision, at what time, and was the result approve, deny or unavailable? The receipt should never infer consent merely from the absence of a refusal.

The fifth stage records the agent verdict: success or failure, time, refusal class and a hash or internal identifier for the signature when appropriate. The sixth records the relying-system result: intended protocol and destination, session identity, verifier decision, requested account and service, whether more authentication was required, later authorization outcome, and the revocation or correction route.

This six-stage receipt is Daniel Kade's governance proposal, not a field set required by RFC 9987. Its purpose is not to make the agent understand business policy. It is to stop one narrow success from impersonating the rest of the chain.

Sources