Bilateral Attestation
When two organizations act together across a trust boundary, one record — owned by one side, deniable by the other — isn't evidence. Bilateral attestation gives each organization proof of the other's commitment, verifiable by a third party who trusts neither.
The problem it solves
Each side keeps its own log. A dispute reduces to my log versus your log. Neither log carries the other party's agreement; either side can later claim the terms were different. The record exists, but it proves nothing a relying party can check independently.
Bilateral attestation replaces that with a four-move exchange that produces a record both parties have signed, each party holds proof of the other's signature, and the combined record can be anchored so a third party who trusts neither organization can verify it end-to-end.
The four moves
Request attestation
The requesting party signs a statement over the action and its material terms, naming the intended performer. The requester is now bound: it cannot later deny having asked, or having asked on these terms.
Constraint evaluation
The performing party verifies the requester's organizational identity and evaluates the request against deterministic constraints at the boundary where the action takes effect — not at the transport edge. No verified request attestation, no consequential action.
Action attestation
The performing party signs a statement referencing the request by digest, recording the constraint results and the disposition — executed, blocked, denied, escalated, timed out. Every outcome is a first-class record; refusals carry the same fidelity as performance.
Acknowledgment
Each party acknowledges the other's attestation. When both acknowledge, each is bound to its part and holds proof of the other's. The record is complete.
action_digest — SHA-256 of the canonicalized action — so a third party who receives both can confirm they describe the same action without either org having to reveal the action's content.Refusals are first-class
A declined request is a completed exchange, not a failed one. The action attestation records that the request was declined and on what constraint basis. The requester's acknowledgment is verifiable evidence contradicting any later claim that the request was never answered.
For the performing party, a bilaterally-acknowledged refusal is stronger evidence than a unilateral one — the counterparty that was refused has signed the record. For the requesting party, a pattern of acknowledged refusals is now provable by its counterparties.
Graceful degradation
Counterparties will be of mixed capability. A performing party whose counterpart cannot produce request attestations may proceed under policy, producing its own action attestation unilaterally and recording a reduced-assurance indicator in place of the missing attestations. The record format is the same; the assurance marking differs. A fully-bilateral record and a degraded record are never confusable.
What it composes with
| Layer | Relationship |
|---|---|
| Agent Action Capsule | Carries the attestations. The disposition vocabulary — executed, blocked, denied, timeout, errored, deferred, escalated — is reused verbatim. |
| Delegation receipts | A request attestation may reference a delegation receipt authorizing the requesting agent. |
| SCITT transparency log | Anchoring both capsules makes the record verifiable by parties who trust neither organization. |
| Remote attestation (RATS) | RATS attests platform state; bilateral attestation attests actions. Orthogonal layers that compose. |
The Internet-Draft
The bilateral attestation exchange is specified as a companion Internet-Draft to the Agent Action Capsule profile:
draft-mih-agent-bilateral-attestation-00 ↗
The draft fixes the exchange, the binding obligations, and the disposition semantics. Wire encodings are TBD for a future revision; the current draft specifies that JCS (RFC 8785) will be used for deterministic canonicalization when wire formats are defined.
Open-source implementation
A reference implementation is available in two packages:
- capsule-emit —
capsule_emit.bilateral:BilateralHandshakestate machine, canonical payload functions, andseal_request/seal_action/seal_bilateralcapsule helpers. - agent-action-capsule —
agent_action_capsule.bilateral:verify_pair(capsule_a, capsule_b)— checks individual Class-1 verify, shared action digest, and chain linkage.
# Run the four-move demo (HMAC-SHA256 demo signer)
pip install capsule-emit agent-action-capsule
git clone https://github.com/action-state-group/capsule-emit
python examples/bilateral/demo.py --no-anchor
# Verify the capsule pair
from agent_action_capsule.bilateral import verify_pair
pvr = verify_pair(capsule_a, capsule_b)
assert pvr.ok
assert pvr.shared_digest == action_digest