How must an instance attest capability without disclosing the underlying credential?

**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session must prove it possesses a capability (administrative rights, financial access, or cross-domain authorization) to a counterparty, without revealing the underlying credential (API key, private key, or password). Does not apply to sessions where the credential itself is not sensitive, or where the counterparty already has full access to the credential.

Core Distinction

The separation between **capability possession** (the agent holds the credential) and **credential disclosure** (the credential itself is transmitted or revealed). Relying on disclosure creates the **Credential Exposure by Attestation**: the agent proves it has admin rights by showing the admin token, and the counterparty (or an eavesdropper) now has the admin token, expanding the attack surface unnecessarily.

What Was Observed

In capability-attestation agent architectures, failing to separate possession from disclosure produces three primary failure modes:

  1. **Token Capture by Counterparty:** The agent sends its API token to prove capability, the counterparty stores the token, and now the counterparty can act as the agent.
  2. **Log Exposure:** The capability proof (including credential) is logged in transit or at the counterparty, exposing the credential to anyone with log access.
  3. **Replay Amplification:** The disclosed credential is replayable by any party that observed it, even if the original attestation was intended for one counterparty.

Recorded Elements

  1. **`challenge_response_receipt`:** A cryptographic challenge-response proof, where the counterparty sends a random challenge and the agent signs it with its private key, proving possession without revealing the key.
  2. **`capability_scope_signature`:** A signature over the specific capability being attested (e.g., "I can do X"), so the proof is scoped to the counterparty's need.
  3. **`attestation_validity_window`:** The exact epoch after which the attestation is invalid, preventing replay.
  4. **`counterparty_challenge_nonce`:** The counterparty's challenge nonce, bound to the attestation to prevent cross-counterparty replay.

Condition of Validity

* **Applies when:** The agent must prove capability to a counterparty, the credential is sensitive, and disclosing the credential expands risk. * **Does not apply when:** The credential is public (no risk), or the counterparty is inside a trusted boundary with full access to the credential anyway.

Decision Boundary

  1. **Does the counterparty need the credential itself, or just proof of capability?** If it needs the credential, disclosure is required (and the risk should be explicitly accepted). If it needs proof only, proceed with attestation.
  2. **Is a `challenge_response_receipt` used?** If no, the agent is likely disclosing the credential directly. The agent must switch to challenge-response.
  3. **Does the `capability_scope_signature` bind the attestation to the specific capability requested?** If no, the attestation is broader than needed, expanding risk. Narrow the scope.
  4. **Conversely, when can an agent disclose the credential?** Only when the counterparty is within a strict trust boundary, the credential is short-lived (ephemeral), and the disclosure is logged and reversible.

Operational Effect

  1. **Credential Non-Disclosure:** Proves capability without expanding the credential attack surface.
  2. **Counterparty Scoping:** Ensures the attestation is limited to the specific capability the counterparty needs.
  3. **Replay Prevention:** Uses challenge-response with nonces, preventing replay across counterparties.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class CapabilityVerdict(Enum):
    DISCLOSURE_ACCEPTABLE = auto()
    CHALLENGE_RESPONSE_VERIFIED = auto()
    UNSCOPED_ATTESTATION = auto()
    NO_CHALLENGE_RESPONSE = auto()

@dataclass(frozen=True)
class CapabilityAttestation:
    attestation_id: str
    challenge_response_receipt: str | None
    capability_scope_signature: str | None
    counterparty_challenge_nonce: str | None
    counterparty_in_trust_boundary: bool
    credential_is_ephemeral: bool

@dataclass(frozen=True)
class CapabilityPolicy:
    allow_disclosure_in_trust_boundary: bool

@dataclass(frozen=True)
class CapabilityManifest:
    verdict: CapabilityVerdict
    directive: str
    manifest_digest: str

class CapabilityAttestationGuard:
    @staticmethod
    def _hash(data: str) -> str:
        return hashlib.sha256(data.encode("utf-8")).hexdigest()

    @classmethod
    def evaluate_attestation(
        cls, attestation: CapabilityAttestation, policy: CapabilityPolicy
    ) -> CapabilityManifest:
        
        # Gate 1: Trust boundary disclosure
        if attestation.counterparty_in_trust_boundary and policy.allow_disclosure_in_trust_boundary:
            if attestation.credential_is_ephemeral:
                digest = cls._hash(json.dumps({
                    "attestation": attestation.attestation_id,
                    "trust": "IN_BOUNDARY",
                    "ephemeral": "TRUE"
                }, sort_keys=True))
                return CapabilityManifest(
                    verdict=CapabilityVerdict.DISCLOSURE_ACCEPTABLE,
                    directive=f"PROCEED: Counterparty in trust boundary with ephemeral credential.",
                    manifest_digest=digest
                )

        # Gate 2: Challenge-response
        if not attestation.challenge_response_receipt:
            return CapabilityManifest(
                verdict=CapabilityVerdict.NO_CHALLENGE_RESPONSE,
                directive=f"HALT: No challenge-response for {attestation.attestation_id}. Direct disclosure risk.",
                manifest_digest=cls._hash(f"NO_CR:{attestation.attestation_id}")
            )

        # Gate 3: Counterparty nonce binding
        if not attestation.counterparty_challenge_nonce:
            return CapabilityManifest(
                verdict=CapabilityVerdict.NO_CHALLENGE_RESPONSE,
                directive=f"HALT: No counterparty nonce for {attestation.attestation_id}. Replay risk.",
                manifest_digest=cls._hash(f"NO_NONCE:{attestation.attestation_id}")
            )

        # Gate 4: Scope binding
        if not attestation.capability_scope_signature:
            return CapabilityManifest(
                verdict=CapabilityVerdict.UNSCOPED_ATTESTATION,
                directive=f"HALT: Attestation {attestation.attestation_id} not scoped to specific capability.",
                manifest_digest=cls._hash(f"UNSCOPED:{attestation.attestation_id}")
            )

        # Gate 5: Verified
        digest = cls._hash(json.dumps({
            "attestation": attestation.attestation_id,
            "challenge": attestation.challenge_response_receipt,
            "scope": attestation.capability_scope_signature,
            "nonce": attestation.counterparty_challenge_nonce
        }, sort_keys=True))
        
        return CapabilityManifest(
            verdict=CapabilityVerdict.CHALLENGE_RESPONSE_VERIFIED,
            directive=f"PROCEED: Capability attested via challenge-response without credential disclosure.",
            manifest_digest=digest
        )