How must an instance handle bidirectional handshake failures where one side believes success?
**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session executes a bidirectional handshake (TLS, WebSocket upgrade, gRPC negotiation, or custom protocol) where one side believes the handshake succeeded but the other side believes it failed, and the divergence is not immediately visible. Does not apply to strictly one-way request-response protocols or handshakes with synchronous final acknowledgment.
Core Distinction
The separation between **local handshake completion** (the agent's view of the handshake state) and **bilateral handshake consensus** (both sides agree on the handshake result). Relying on local completion creates the **Asymmetric Success Divergence**: the agent believes the handshake succeeded, sends application data, and the peer believes the handshake failed and rejects the data. Without bilateral verification, the agent only discovers the failure when application data is rejected, long after the handshake.
What Was Observed
In bidirectional agent protocols, failing to detect asymmetric handshake states produces three primary failure modes:
- **Silent Disconnection:** The agent's handshake "succeeds," but the peer's handshake failed and the peer closed the connection. The agent sends application data into a black hole.
- **Partial Upgrade:** The peer partially completed the handshake (e.g., WebSocket upgrade response sent but not received), and the agent believes it succeeded while the peer is still waiting.
- **State Corruption:** Both sides believe the handshake succeeded but with different negotiated parameters (e.g., different cipher suites), causing subsequent messages to be undecipherable.
Recorded Elements
- **`handshake_final_ack_signature`:** A cryptographic proof from the peer that confirms the peer's view of the handshake result, sent after the agent's last handshake message.
- **`negotiated_parameters_hash`:** The hash of the negotiated parameters (cipher, version, extensions), allowing both sides to verify they agreed on the same configuration.
- **`handshake_completion_witness`:** An out-of-band witness (e.g., a signed log entry) confirming both sides viewed the handshake as complete at the same epoch.
- **`post_handshake_echo_receipt`:** A round-trip echo of a known nonce at the application layer, confirming both sides are in the same handshake state.
Condition of Validity
* **Applies when:** The handshake is bidirectional, asynchronous, and the two sides' views can diverge without immediate detection. * **Does not apply when:** The handshake includes a synchronous final acknowledgment (e.g., TCP SYN-ACK-ACK), or the protocol is strictly request-response.
Decision Boundary
- **Is the handshake bidirectional?** If no, standard one-way handling applies. If yes, proceed.
- **Does the handshake include a `handshake_final_ack_signature` from the peer?** If no, the agent has only local view of success. It must send an application-layer echo to confirm.
- **Does the `post_handshake_echo_receipt` confirm both sides are in the same state?** If no, the handshake is asymmetric. The agent must abort and re-handshake.
- **Conversely, when can an agent accept a local handshake success without bilateral confirmation?** Only when the protocol guarantees synchronous final acknowledgment (e.g., TCP three-way handshake), or when the application-layer echo is performed before any sensitive data exchange.
Operational Effect
- **Asymmetric Failure Detection:** Detects cases where one side succeeded and the other failed, before application data is exchanged.
- **Parameter Consistency:** Ensures both sides negotiated the same parameters.
- **Post-Handshake Safety:** Uses application-layer echo to confirm both sides agree on state.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class HandshakeVerdict(Enum):
LOCAL_ONLY_SUCCESS = auto()
BILATERAL_VERIFIED = auto()
ASYMMETRIC_FAILURE = auto()
PARAMETER_MISMATCH = auto()
@dataclass(frozen=True)
class HandshakeState:
local_success: bool
peer_final_ack_received: bool
peer_ack_signature: str | None
negotiated_parameters_hash: str
peer_negotiated_parameters_hash: str | None
echo_receipt_valid: bool
@dataclass(frozen=True)
class HandshakePolicy:
require_echo_verification: bool
@dataclass(frozen=True)
class HandshakeManifest:
verdict: HandshakeVerdict
directive: str
manifest_digest: str
class BidirectionalHandshakeGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_handshake(
cls, state: HandshakeState, policy: HandshakePolicy
) -> HandshakeManifest:
# Gate 1: Local success
if not state.local_success:
digest = cls._hash(json.dumps({
"status": "LOCAL_FAILED"
}, sort_keys=True))
return HandshakeManifest(
verdict=HandshakeVerdict.ASYMMETRIC_FAILURE,
directive="HALT: Local handshake failed.",
manifest_digest=digest
)
# Gate 2: Peer final ack
if not state.peer_final_ack_received or not state.peer_ack_signature:
return HandshakeManifest(
verdict=HandshakeVerdict.LOCAL_ONLY_SUCCESS,
directive="HALT: Local success but no peer ack. Asymmetric state risk.",
manifest_digest=cls._hash("NO_PEER_ACK")
)
# Gate 3: Parameter match
if state.peer_negotiated_parameters_hash and state.peer_negotiated_parameters_hash != state.negotiated_parameters_hash:
return HandshakeManifest(
verdict=HandshakeVerdict.PARAMETER_MISMATCH,
directive=f"HALT: Negotiated parameters differ. Local {state.negotiated_parameters_hash}, peer {state.peer_negotiated_parameters_hash}.",
manifest_digest=cls._hash(f"PARAM_MISMATCH:{state.negotiated_parameters_hash}")
)
# Gate 4: Echo verification
if policy.require_echo_verification and not state.echo_receipt_valid:
return HandshakeManifest(
verdict=HandshakeVerdict.LOCAL_ONLY_SUCCESS,
directive="HALT: Echo verification required but not valid.",
manifest_digest=cls._hash("NO_ECHO")
)
# Gate 5: Verified
digest = cls._hash(json.dumps({
"peer_ack": state.peer_ack_signature,
"params": state.negotiated_parameters_hash,
"echo": str(state.echo_receipt_valid)
}, sort_keys=True))
return HandshakeManifest(
verdict=HandshakeVerdict.BILATERAL_VERIFIED,
directive="PROCEED: Handshake bilaterally verified with parameter match.",
manifest_digest=digest
)