| Internet-Draft | counter-sign | July 2026 |
| Kumaresan | Expires 22 January 2027 | [Page] |
Autonomous software agents can now take consequential actions on an organization's behalf, such as moving money or changing production systems. Existing agent protocols standardize how an agent reaches tools and how agents reach one another, but not how a specific action is authorized by a human in a way that stays verifiable after the fact. This document describes counter-sign, an open protocol for agent-to-human authorization. An agent emits a signed Intent that states the action it proposes to take. One or more humans return signed Countersignatures that authorize the action, each signed with a key the coordinating service does not hold. Every decision produces a tamper-evident receipt that can be verified offline. The protocol defines the Intent and Countersignature objects, a per-approver-key quorum model for separation of duty, an enrollment registry that binds actors to keys, and a hash-chained receipt log. Conformance test vectors accompany the specification.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 22 January 2027.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Deployments of autonomous agents increasingly place a human "in the loop" before a high-stakes action executes. In practice, that approval step is improvised: a button in a chat tool, or a webhook that sets a flag inside one vendor's platform. Such approvals are usually not verifiable after the event, and they rarely survive an independent audit. In many designs, the service that requested the approval can also fabricate it.¶
counter-sign specifies this missing step. Its central design principle is to separate proposing an action from authorizing it. An Agent MAY propose an action with full precision but MUST NOT be able to authorize it; authorization is conveyed only by a Countersignature produced by a key held by an approving party. The artifact of authorization is a signed receipt that any relying party can verify offline using public keys alone.¶
This category of infrastructure -- cryptographic, human-named, mandate-scoped accountability chains for autonomous agents -- is what the authors call Agent Accountability Rails (AAR). counter-sign specifies one such rail: the agent-to-human authorization step. The pattern generalizes: every prior emerging technology that reached regulated adoption did so by shipping an accountability rail of the same shape (aviation's flight plan and tail number; automotive's VIN and driver's license; payments' PCI-DSS and tokenization; commercial drones' Part 107 licensing). Autonomous agents are at the moment right before their accountability rails exist. counter-sign is a proposed first component.¶
This document does not specify:¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
The protocol has four nouns in one flow: Intent, Route, Countersignature, and Default. An Agent signs an Intent; the Intent is Routed to the required Approver(s); each Approver returns a Countersignature; if no resolving decision arrives before the timeout, the signed Default applies. Every terminal outcome produces a receipt.¶
The core invariant is separation of duty: for a quorum greater than one, each approval is a distinct signature produced by a distinct key that the coordinating (Authority) service does not hold. A party in possession of the Authority key therefore cannot forge a quorum or approve on an approver's behalf.¶
All signatures are computed over a deterministic, domain-separated canonical serialization of the object being signed. Implementations MUST produce byte-identical canonical forms for equal objects. The exact canonical JSON rules and byte encoding are defined normatively by the specification (comparable in approach to the JSON Canonicalization Scheme [RFC8785]) and are exercised by the canonical-JSON known-answer conformance vectors (Appendix A).¶
Signatures use Ed25519 [RFC8032]. Public keys are encoded as base64url without padding. Every signature commits to an artifact-type signing context (a domain-separation label) so that a signature created for one object type, such as an Intent, cannot be replayed as another, such as a Countersignature or a delivery link. Human approvers MAY sign using a WebAuthn assertion [WebAuthn] from a platform authenticator (a "passkey"); the private key never leaves the approver's device and is never transmitted to the Authority.¶
An Intent is emitted by an Agent before any consequential action. It binds the action to its authorization policy and is signed by the Agent key. An Intent carries, at minimum: a protocol version; a unique intent identifier; the action and a human-readable summary; a risk tier; the set of permitted approvers; the required quorum; a timeout; and the default outcome. An Intent MUST be treated as a request only; possession or verification of an Intent conveys no authority.¶
The Enrollment Registry is an append-only, hash-chained log that binds each actor to the key(s) it is authorized to sign with. Each binding MUST be attested by an Org-root key that is distinct from the Authority key, and MUST include proof of possession of the enrolled key (a raw Ed25519 self-signature, or a WebAuthn assertion for a passkey). The registry supports revocation. A verifier uses the registry to establish which key legitimately speaks for a given actor.¶
A Countersignature conveys one party's decision, bound to a specific Intent, and signed by that party's key. It carries: the decision (approve or reject); the acting actor; the policy under which it was produced; a binding to the exact Intent; and a timestamp. A keyed Countersignature is signed by the approver's own enrolled key. A verifier MUST confirm both the signature and the actor-to-key binding before treating a Countersignature as authoritative.¶
For a quorum M greater than one:¶
These rules are exercised by accept and reject conformance vectors, including under-quorum, forged-quorum, and wrong-authority-key cases.¶
Every Intent declares a signed Default and a timeout. If the timeout elapses without a resolving decision, the Default applies and MUST itself produce a signed receipt, so that a timeout is never an ambiguous outcome.¶
Receipts MAY be recorded in an append-only, hash-chained Receipt Log. Each receipt is independently tamper-evident through its own signature; the chain additionally makes any reorder, edit, or deletion detectable relative to an externally anchored head. Verification distinguishes authenticity of individual receipts from completeness of the log against a caller-held anchor (expected head). A keyless hash chain is not, by itself, standalone tamper-evidence: its integrity claim is only as strong as the anchor a relying party retains.¶
A relying party resolves an Intent to "authorized" or "denied" by: (1) verifying the Intent's Agent signature and canonical form; (2) collecting Countersignatures and verifying each signature and actor-to-key binding against the Enrollment Registry; (3) applying the quorum and veto rules of Section 8; and (4) applying the Default if the timeout has elapsed. A relying party MUST NOT act on an Intent that has not resolved to "authorized", and MUST NOT treat an unverifiable receipt as a decision.¶
An implementation claims conformance by reproducing the accompanying conformance test vectors (Appendix A). The vectors are deterministic and language-neutral: fixed test keys, canonical-JSON known-answers, signing-context message and signature vectors, signed Intents, keyed/vouched/default receipts, resolution accept and reject cases, and the receipt-log chain head. A conforming implementation MUST pass all applicable vectors. The reference implementation and vectors are available at [COUNTERSIGN].¶
The principal threat this protocol addresses is a compromised or dishonest coordinating (Authority) service. Because each quorum approval is signed by a key the Authority does not hold, a compromised Authority cannot forge a quorum or self-approve; at most it can withhold or delay routing, which fails closed to the signed Default.¶
Signatures are domain-separated by signing context to prevent cross-protocol signature replay. Intents SHOULD carry a unique identifier and be bound to their receipts to prevent replay of a stale approval against a new action. Key rotation and revocation are handled through the Enrollment Registry. Non-repudiation extends only to the keys enrolled for an actor; compromise of an approver's device key is out of scope for the protocol and is mitigated by platform authenticator protections and revocation. The v0.1-to-v0.2 change (see Appendix C) is a deliberate, non-backward-compatible break: prior integrity-only receipts were forgeable by a compromised Authority and MUST be rejected by a v0.2 verifier rather than silently accepted.¶
Receipts reveal the acting approver's identity, the action, and timing to any party that holds them. Deployments SHOULD minimize the identifying content carried in Intents and receipts. Selective disclosure of receipt fields is future work.¶
This document has no IANA actions at this time. A future revision may request registration of the protocol's signing-context labels and any media types used for the Intent and Countersignature objects.¶
The normative conformance vectors are published with the reference implementation at <https://github.com/countersign-labs/counter-sign/tree/main/vectors>. A future revision of this document MAY inline them or a stable subset.¶
These examples are illustrative. Field names and encodings are shown for clarity; the normative encoding is defined by the specification and the conformance vectors (Appendix A). Identifiers and signatures are abbreviated.¶
An autonomous content agent proposes publishing a public brand post. Because the action communicates publicly on the organization's behalf, policy requires two distinct approvers, each signing with their own enrolled key. The Agent emits and signs the Intent:¶
{
"countersign": "0.2",
"intent_id": "3f0a2c7e-1d54-4b8a-9e02-6c1b7ad9e921",
"action": "social.post.publish",
"summary": "Publish the Q3 campaign post to the company page",
"risk_tier": "high",
"approvers": [
{ "actor": "m:cmo", "key": "ed25519:aF92...6c1" },
{ "actor": "m:brand-lead", "key": "ed25519:71bd...d3e" }
],
"quorum": 2,
"timeout": 900,
"default": "reject",
"agent_sig": "ed25519:7bc1...e94a"
}
¶
Each approver reviews the request and returns a Countersignature signed with their own enrolled key:¶
{ "countersign": "0.2",
"intent_id": "3f0a2c7e-...-e921",
"decision": "approve",
"actor": "m:cmo",
"time": "2026-07-21T09:14:07Z",
"actor_sig": "ed25519:2ce7...a10b" }
{ "countersign": "0.2",
"intent_id": "3f0a2c7e-...-e921",
"decision": "approve",
"actor": "m:brand-lead",
"time": "2026-07-21T09:15:42Z",
"actor_sig": "ed25519:9f04...77dc" }
¶
The relying party verifies each signature and each actor-to-key binding against the Enrollment Registry, confirms the two approvers are distinct, and finds the quorum of two satisfied. The Intent resolves to authorized; both Countersignatures are appended to the hash-chained Receipt Log, and the post is published.¶
An agent proposes a state-changing API call. Rather than executing it, the caller captures the call as an Intent and holds it pending authorization. The Intent requires one enrolled approver:¶
{
"countersign": "0.2",
"intent_id": "b41d0e6f-77a2-4c19-8f5e-0aa3d51902af",
"action": "api.request.execute",
"summary": "POST /v1/payouts - release vendor payout",
"risk_tier": "high",
"approvers": [
{ "actor": "m:controller", "key": "ed25519:c3a8...9f2" }
],
"quorum": 1,
"timeout": 600,
"default": "reject",
"agent_sig": "ed25519:5da2...1b7c"
}
¶
No approver responds within the 600-second deadline. The Default applies, and the Authority records a signed timeout receipt so that the silence is itself an auditable outcome:¶
{ "countersign": "0.2",
"intent_id": "b41d...02af",
"decision": "reject",
"actor": "default:timeout",
"time": "2026-07-21T11:03:11Z",
"authority_sig": "ed25519:0ab9...4e5f" }
¶
The Intent resolves to denied, the captured API call is never executed, and the timeout receipt is appended to the Receipt Log.¶
Version 0.2 introduces per-approver-key quorum (cryptographic separation of duty), the Enrollment Registry, and experimental passkey/WebAuthn human signing. It is a deliberate wire-format break from v0.1: v0.1 receipts were integrity-only and forgeable by a compromised Authority, and are rejected by a v0.2 verifier.¶