Skip to content

Verified client identities (self-sovereign clientId via did:key) #259

Description

@connor4312

Summary

Add an opt-in, transport-agnostic mechanism for clients to prove a stable, self-sovereign identity to an agent host. Today clientId is an unauthenticated string, so any peer that learns another client's clientId can impersonate it — most notably to hijack reconnect and replay another client's session deltas.

This proposal gives clients a cryptographic identity that proves continuity ("the same client is connecting again"). Authorization is out of scope — a host (or a proxy in front of it) decides whether a given identity is allowed by its own means.

Mechanism

  • A client's identity is a did:key used as its clientId (e.g. did:key:z6Mk...). The public key is embedded in the identifier, so no separate key field is needed.
    • Baseline: Ed25519 (MUST). P-256 allowed (Secure Enclave / TPM / WebAuthn hardware keys). did:key is self-describing, leaving room for post-quantum schemes later.
    • The did:key: prefix is the unambiguous marker for "identity-bearing clientId."
    • The resulting client ID's are 56 characters or 57 characters for Ed25519 and P-256 respectively.
  • Proof is a server-issued challenge carried in a new error, symmetric for initialize and reconnect:
    1. Client sends initialize / reconnect with a did:key clientId.
    2. If the host enforces identity and no valid proof is attached, it replies with ClientIdentityChallengeRequired (-32012) and data: { nonce } (opaque, base64url).
    3. Client signs "AHP-client-identity-v1" ‖ nonce and retries the same command with an added identityProof.
    4. Host verifies the signature against the key in the clientId, then proceeds.
  • The signed payload is always wrapped by the fixed spec prefix "AHP-client-identity-v1". The host controls the nonce body; the prefix guarantees the signature can only ever be an AHP identity proof (no blind-signing oracle).

Host requirements

  • The nonce MUST be single-use and bound to the connection (e.g. by embedding a connection id), so a captured (nonce, signature) pair can't be replayed on another connection.
  • The nonce MAY be a stateless authenticated token (random ‖ HMAC_serverKey(random ‖ clientId ‖ connId ‖ expiry)), avoiding server-side challenge storage.
  • A host that enforces identity MUST reject a did:key-formatted clientId presented without a valid proof.

Compatibility

Non-breaking and opt-in:

  • Hosts unaware of the scheme ignore the extra field and accept the connection.
  • Clients not adopting identities keep using plain clientId strings.
  • The security guarantee holds only where the host enforces it.

Transport security (non-normative)

AHP does not specify a transport. Where the transport already authenticates the client (mutual TLS, OS peer credentials on a local socket), a host MAY treat that as satisfying identity and skip the challenge. A TLS-terminating host MAY also channel-bind by folding a TLS exporter value into the opaque nonce — mTLS-grade binding with no transport language in the spec.

Proposed surface

  • types/common/errors.ts: add ClientIdentityChallengeRequired: -32012 to AhpErrorCodes and a ClientIdentityChallengeData { nonce: string } detail type.
  • types/common/commands.ts: add optional identityProof to InitializeParams and ReconnectParams.
  • Docs: new proposal under docs/proposals/, plus a Lifecycle handshake update once accepted.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestunder-discussionIssue is under discussion for relevance, priority, approach

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions