Summary
- RFC 9987 standardises a service that keeps private keys in an agent but accepts requests to list, load, remove and use them. The baseline protocol has no authentication or transport protection; access to the endpoint is normally the operative permission.
- Forwarding does not copy the secret key to a remote host. It extends the ability to request signatures across an SSH connection, creating the transitive trust that the RFC itself warns operators not to enable by default.
- Lifetime, per-use confirmation and extension constraints can narrow that capability, while the destination server still decides whether a key and signature authenticate an account. None of those results proves the next one without evidence.
Consider a routine administrative path. A developer unlocks one key on a laptop, connects to a bastion and asks the SSH client to forward the local agent. From the bastion, a second SSH client can reach a socket that appears local. Behind that socket is a channel back to the laptop’s agent. The bastion never receives the private scalar, seed or prime factors. It receives something operationally valuable instead: a path for asking the key holder to perform a private-key operation.
Now assume a process on the bastion has been compromised. The forensic team searches its disk and memory and finds no private key. That is useful evidence about extraction. It says nothing yet about use. If the process reached the forwarded socket while the agent was available, it could have submitted a signature request. If another server accepted the corresponding public key for an account, a newly generated signature could have completed a fresh authentication exchange. The secret stayed home; the decision surface travelled.
RFC 9987, published as a Proposed Standard in May 2026, makes this distinction unusually explicit. Its agent is a key-management component. Clients may ask it to enumerate identities, load or remove keys, lock or unlock the service and perform signatures. The agent is reactive: it sends no unsolicited message and replies to requests in order. That modest wire contract is valuable because it standardises a widely deployed interface without pretending that the interface supplies a whole authorization system.
A vault with a callable aperture
The agent’s security benefit is real. An application does not need to keep an unwrapped private key in its own memory. A dedicated process can be smaller, can resist dumps and debugging, and can isolate token-provider code. A user need not type a passphrase for every new SSH connection. Hardware-backed keys can remain on a token.
But isolation changes the form of exposure; it does not abolish it. The RFC says the agent protocol itself includes neither authentication nor transport security. On a Unix-like system, a filesystem socket and peer-credential checks are expected to keep access within the owner’s boundary. On Windows, a named pipe can carry a security descriptor. SSH_AUTH_SOCK commonly tells clients where the endpoint lives. These controls are not decoration around the credential. They determine who can invoke it.
This yields two different incident questions:
- Could an attacker obtain a reusable copy of the private key?
- Could an attacker make the legitimate key holder perform operations during the exposure window?
The first concerns material custody. The second concerns callable authority. A non-exportable token may answer the first question favourably while leaving the second open. RFC 9987 even describes the distinction directly: an agent may prevent theft of loaded keys without preventing theft of their use.
The private-operation message reinforces the point. A client supplies a public-key blob, a string of data and signature flags. The agent can return a signature or refuse. At this layer, success means the requested key produced a signature under the selected method. It does not mean that the data represented an approved destination, that the requesting process was entitled to act for a human, that a server accepted the key, or that a command ran.
Constraints are state, not adjectives
RFC 9987 defines two compact baseline constraints. A lifetime constraint asks the agent to delete a loaded key after a specified number of seconds. A confirmation constraint asks for explicit user confirmation before every private-key operation. Named extension constraints can add implementation-specific limits.
These are useful controls because they move refusal closer to the private operation. They are also narrower than the assurance language often built around them.
A lifetime is measured from key loading. It closes future access at that agent after expiry. It does not revoke a signature already issued, disconnect a session already accepted by a server, reverse a command or prove that another agent did not load the same credential.
A confirmation gate adds a human decision. The protocol does not define the dialogue, the context displayed or the meaning a user assigns to approval. A prompt that clearly names a destination, account and request can support a stronger verdict than one that merely asks whether a key may be used. High request volume can turn confirmation into reflex. The evidence must record what the implementation actually showed and what the user actually approved, not just the presence of a “confirm” setting.
Extension constraints have a particularly sound failure rule. Because an unknown constraint cannot be safely skipped, an agent must stop parsing and reject the key. That protects the user from a client attaching a restriction that the agent silently ignores. Yet the restriction works only when both sides implement it, and its running enforcement still needs a test.
Constraint state can also change. If a key already exists, a later add request should replace its previous constraints with the new set, or the agent may refuse the duplicate. An operator who inspected a constrained key at login cannot assume those limits survived a later reload. The durable record is the agent’s effective state after each transition.
Forwarding changes the trust graph
Agent forwarding connects the remote side of one SSH connection to the client-side agent. The private key is not sent through that channel. Agent protocol messages are. A remote process can therefore make a local-looking request that travels back to the key holder.
The RFC calls the result an inherent transitive trust relationship. Its operational advice is direct: implementations should not forward by default, and users should not forward to hosts they do not fully trust. Without extra visibility and use controls for forwarded connections, the choice becomes all or nothing.
The word “transitive” matters. Trusting a bastion to run a shell is not automatically the same as trusting every privileged process, extension, administrator and compromised workload on that bastion to call every forwarded key. Forwarding can also be multiplexed. Several sessions over one SSH transport may create concurrent connections to the same client-side agent.
Attribution is not supplied for free. The agent-connect request does not carry an identifier for the session channel that originated it. A client may, subject to authorization, accept a connection without a prior session request and may continue accepting connections after the session that asked for forwarding has closed. These behaviours can be legitimate, but they make “the key was used from this shell” a claim that must be reconstructed, not assumed from topology.
The minimum trace should join the outer SSH connection, the forwarding request, every agent channel, the key fingerprint, constraint state, signature request, destination authentication record and resulting channel. Clock alignment and connection identifiers matter. Otherwise, a team may prove that forwarding existed and that a login succeeded without proving which remote process caused which signature.
A signature request is not the server’s verdict
SSH public-key authentication supplies an important boundary on the other side. Under RFC 4252, the client signs a structured authentication request containing the SSH session identifier, user name, service, method, algorithm and public-key blob. The destination server must decide whether that key is an acceptable authenticator for the requested user and whether the signature is valid. It may require more authentication.
This prevents a responsible analysis from treating every agent signature as universal login authority. The exact authentication blob is bound to one session and request. A signature generated for one exchange is not simply a reusable password. At the same time, a caller with live agent access can ask for another signature over another correctly formed exchange while the capability remains available.
Five states must therefore stay separate:
- the private key exists in the agent;
- a process can reach the agent endpoint;
- the agent agrees to sign one input under its current constraints;
- the destination accepts the public key and the signature for an account;
- an authenticated session receives permission to open channels and cause effects.
Algorithm support adds another axis. The agent may hold an RSA, Ed25519 or other supported key; flags can request an RSA/SHA-2 signature method; the server may accept or reject that method; fleet policy may be narrower still. An IANA number or RFC status coordinates names and formats. It does not prove that a particular endpoint enables, accepts or recommends them.
What a complete incident record looks like
The phrase “no private key left the device” belongs in a report, but it cannot close the credential question. A complete record asks what happened at each executable boundary.
At the origin, identify the agent process, endpoint permissions, connected peer identity, loaded key fingerprint, time of loading, effective constraints, lock state and removal time. If the key came from a token, record which provider component could invoke it and whether remote clients could load providers.
At the forwarding boundary, identify the outer connection, host trust decision, forwarding request, accepted agent channels, their lifetime and any connections that persisted after a session channel closed. Record whether forwarding was explicitly requested, inherited from configuration or attempted opportunistically.
At the operation boundary, retain the request time, key, signature algorithm, safe fingerprint of the signed context, decision under each constraint and the confirmation outcome. Raw sensitive data need not be sprayed into a general log, but the record must be strong enough to join the operation to a destination authentication exchange.
At the relying server, preserve the requested account, accepted key, authentication result, extra factors, source path, session identifier or equivalent correlation, opened channels and server-side restrictions. Then observe the outcome: which command, subsystem, port forward or file operation was allowed or refused.
Finally, prove termination. Remove the key, close the agent, end forwarding channels, revoke or remove destination authorizations where necessary and verify that new attempts fail. Do not mistake local deletion for closure of sessions that have already crossed the boundary.
This is running-code evidence. It replaces a symbolic statement about where a secret resided with a replayable account of what the credential could do.
Member Briefing
Deeper Profile Context
Sign in with the right membership level to unlock the full briefing and source notes.
Only for Strategic Circle
Strategic Circle
Open to all readers. Unlock profile briefings after joining and signing in.
Join Strategic CircleOnly for Leadership Alliance
Leadership Alliance
For qualified IP-asset owners and management; sign in to unlock alliance briefings.
Join Leadership Alliance
