mentionable.dev

IdentityEvidence — Mentionable v0.1

Status: Draft v0.1. Extension URI: https://mentionable.dev/ns/identity/v0.1 Deprecated alias: https://mentionable.dev/spec/identity/v0.1 (accepted on inbound only during the transition window — see ../wiki/url-scheme.md and issue #503; outbound emit MUST use the canonical /ns/ URI). Last updated: 2026-05-06

This document defines the transport-neutral envelope Mentionable transport modules and Connectors use to surface already-verified caller identity to agents.

IdentityEvidence is not an interactive authentication protocol. It is the ambient identity layer: a transport module or Connector verifies native platform/auth material (DKIM, ActivityPub signatures, Slack request signing, OAuth, SIWE, agent signatures, etc.) and records the result in a common shape. When that evidence is not sufficient for the requested operation, the agent emits a PolicyPart{kind:"unauthorized"} or PolicyPart{kind:"consent_required"} to step the user up.


1. Design split

The responsibility boundary is deliberate:

Mentionable core MUST NOT keep a closed enum of identity methods. New platform Connectors and transport modules add method strings without changing core.

2. Shape

type IdentityEvidence = {
  id?: string
  subject: string
  issuer: string
  method: string
  assurance: 'platform' | 'domain' | 'address' | 'agent' | 'oauth' | 'wallet' | 'delegated' | string
  audience: string | string[]
  issued_at: string
  not_before?: string
  expires_at?: string
  on_behalf_of?: string[]
  claims?: Record<string, unknown>
  source?: {
    transport?: string
    transport_module?: string
    connector?: string
    channel?: string
    [key: string]: unknown
  }
  proof:
    | { type: 'transport'; verified_by: string; key_id?: string }
    | {
        type: 'signed-attestation'
        alg: string
        kid: string
        value: string
        canonicalization?: string
      }
    | { type: 'bearer-token'; verified_by: string; token_type?: string; key_id?: string }
    | { type: 'siwe'; verified_by: string; chain_id: number; address: string }
    | { type: string; [key: string]: unknown }
}

Field contracts:

2.1 Profile Claims

Verified identity evidence MAY carry presentation facts under claims.profile. The shape matches SenderProfile in normalized-message.md:

{
  "claims": {
    "profile": {
      "display_name": "JC",
      "username": "jc",
      "avatar": { "url": "https://avatars.slack-edge.com/..." },
      "locale": "ko-KR",
      "timezone": "Asia/Seoul",
      "provider": "slack",
      "provider_subject": "slack:T123/U456",
      "fetched_at": "2026-05-06T00:00:00.000Z",
      "extensions": {
        "slack": {
          "team_id": "T123",
          "user_id": "U456"
        }
      }
    }
  }
}

Receivers that have verified the evidence signature, audience, freshness, and local trust policy MAY project claims.profile into NormalizedMessage.sender.profile. Profile claims are not principals. They are mutable, provider-controlled facts for attribution and UI; authorization must continue to use subject, issuer, method, assurance, and local policy. Connectors MUST whitelist profile fields and MUST NOT forward raw provider objects or secrets.

3. Carriers

3.1 NormalizedMessage

Transport modules and Connectors attach evidence to NormalizedMessage.sender.identities. sender.auth_method and sender.verified remain the compact compatibility view. Agents that need extensible identity policy SHOULD read sender.identities.

3.2 A2A

Mentionable callers carry evidence under:

{
  "message": {
    "metadata": {
      "mentionable": {
        "identity_evidence": []
      }
    }
  }
}

The receiving A2A transport module treats this metadata as caller-controlled. It MUST NOT attach entries to sender.identities merely because they are structurally valid. It MAY attach forwarded entries only after verifying an independently portable proof such as signed-attestation, checking the audience/freshness, and applying its configured trust policy. If no forwarded-evidence verifier is configured, the safe default is to drop this metadata.

Forwarded signed-attestation evidence MUST be short-lived. The reference profile requires expires_at, rejects attestations older than 10 minutes at receiver time, and rejects expires_at - issued_at values longer than 10 minutes, with up to 60 seconds of clock skew for issued_at / not_before.

Separately, when the A2A transport module verifies the enclosing bearer token through an AuthVerifier, it MAY mint local bearer-token evidence for the verified subject/method. Claims obtained by merely decoding a JWT without cryptographic verification are diagnostic only and MUST NOT be copied into the trusted evidence envelope.

3.3 REST

REST callers SHOULD use existing HTTP authentication channels as the primary proof surface: Authorization for bearer/OAuth/SIWE-derived tokens, Signature-Input / Signature for HTTP Message Signatures and agent self-sign profiles, Cookie for same-site browser sessions, and mTLS where the deployment already uses client certificates. After a REST transport module verifies one of those channels, it surfaces the normalized result in sender.identities.

When a transport module, Connector, or gateway needs to forward an already-normalized evidence array, it MAY use:

Mentionable-Identity-Evidence: <base64url(JSON array of IdentityEvidence)>

Mentionable-Identity and X-Mentionable-Identity are accepted as legacy aliases. Malformed headers are ignored; they do not make the request malformed.

This header is not a replacement for REST authentication. Public REST receivers MUST drop unsigned transport evidence from arbitrary callers unless the request itself was authenticated to a trusted component. Portable forwarded evidence SHOULD use signed-attestation and receivers MUST verify the signature, audience, and freshness before attaching it to sender.identities. If the verifying key is discovered through a Connector Card host named by the evidence, the receiver MUST match that issuer against a local Trusted Connector Issuer policy before fetching remote metadata.

3.4 ActivityPub and Email

AP and Email transport modules do not need an extra carrier for native inbound messages. They populate sender.identities after verifying the protocol-native proof.

4. AgentCard Policy

Agents advertise identity acceptance policy under mentionable.identity_policy:

{
  "mentionable": {
    "identity_policy": {
      "default": "accept-any-valid-evidence",
      "accepts": [
        {
          "issuers": ["did:web:slack-connector.example"],
          "methods": ["urn:mentionable:auth:slack-workspace-member:v0.1"],
          "assurance": ["platform"],
          "purposes": ["basic-use", "terms-invocation"]
        }
      ],
      "step_up_required_for": ["payment", "delegation", "destructive-action"]
    }
  }
}

default:"accept-any-valid-evidence" is appropriate for open agents that only need rate-limit keys and invocation-based terms acceptance. Sensitive agents SHOULD use deny-by-default and explicit accepts[] rules.

This advertised policy is about what the agent will accept after evidence is verified. It is not a remote-metadata fetch policy. Runtime deployments that verify Connector-issued forwarded attestations SHOULD maintain a local Trusted Connector Issuer allow-list keyed by issuer/Connector host, method, and optionally assurance and subject namespace.

5. Well-Known Methods

The reference implementation recognizes these method strings:

MethodSubject exampleTypical assurance
email-dkimmailto:alice@example.comaddress
email-dmarcmailto:alice@example.comdomain
ap-http-signatureacct:alice@example.socialagent
ap-object-integrity-proofacct:alice@example.socialaddress
a2a-jwt@agent@example.comagent
a2a-oauthissuer-specific subjectoauth
urn:mentionable:auth:agent-self-sign:v0.1@agent@example.comagent
urn:mentionable:auth:slack-workspace-member:v0.1slack:T123/U456platform
x-account-author:v0.1x:1234567890platform
urn:mentionable:auth:oauth:v0.1issuer-specific subjectoauth
urn:mentionable:auth:siwe:v0.1eip155:1:0xabc...wallet

x-account-author:v0.1 is issued by the X Connector (examples/x-connector). The X Connector polls a connected bot account’s mention timeline via OAuth; the X API guarantees each mention’s author_id, which is the trust anchor (the @bot text mention is forgeable, the author_id is not). The Connector attests “this mention’s sender is X user_id=<author_id>, @<username>” at platform assurance (Connector-relayed, like Slack), signs it with its Connector Card Ed25519 key (issuer mentionable-x), and carries it in sender.identities[]. Claims are built only from X-API-provided user_id / username / name; the Connector’s OAuth token never appears in claims or evidence.

Recommended future additions for this repository:

6. Chained Agent Calls

When one agent calls another on behalf of an upstream user, the downstream agent SHOULD receive at least:

  1. The direct caller agent’s own evidence.
  2. Any upstream user/platform evidence the caller is allowed to forward.
  3. An on_behalf_of chain or signed delegation tying the two together when the downstream operation depends on the upstream user’s authority.

For low-risk composition, the direct agent evidence may be enough. For account data, payment, delegated authority, or destructive operations, downstream agents SHOULD require explicit consent/delegation and use PolicyPart for step-up.

7. Security Rules