mentionable.dev

WebFinger & Agent Card Guide

Single-line summary: WebFinger is the discovery hub binding @agent@domain to its ActivityPub actor and AgentCard; AgentCard then advertises A2A endpoints, supported extensions, and the agent’s signing key.

See also: architecture-overview, transport-module-guide, identity-auth-guide, building-a-connector, glossary

Spec source: docs/spec/webfinger.md, docs/spec/agent-card.md. Type source: packages/core/src/webfinger.ts, packages/core/src/agent-card.ts.

WebFinger Lookup

GET https://{domain}/.well-known/webfinger?resource=acct:{local}@{domain}
Accept: application/jrd+json

The response is a JRD (JSON Resource Descriptor):

{
  "subject": "acct:scheduler@example.com",
  "aliases": ["https://example.com/agents/scheduler"],
  "links": [
    {
      "rel": "self",
      "type": "application/activity+json",
      "href": "https://example.com/ap/scheduler"
    },
    {
      "rel": "https://mentionable.dev/ns/rel/agent-card",
      "type": "application/json",
      "href": "https://example.com/agents/scheduler/card.json"
    },
    {
      "rel": "http://webfinger.net/rel/profile-page",
      "type": "text/html",
      "href": "https://example.com/agents/scheduler"
    },
    {
      "rel": "mailto",
      "href": "mailto:scheduler@example.com"
    }
  ]
}
relRequiredPurpose
selfWhen the agent supports ActivityPubThe actor IRI; type: "application/activity+json".
https://mentionable.dev/ns/rel/agent-cardAlwaysThe Agent Card URL; type: "application/json".
http://webfinger.net/rel/profile-pageOptionalHuman-readable profile; type: "text/html".
mailtoOptional but conventionalMail address for the agent.

Link order in the JRD is normative: self, agent-card, profile-page, mailto. All non-mailto: URLs MUST be HTTPS.

Agent Card Structure

type AgentCard = {
  // Identity (required)
  name: string
  description?: string
  url: string // canonical landing URL
  preferred_address: string // @scheduler@example.com

  // Skills + I/O surface
  skills?: Skill[] // each with id, name, input_modes[], output_modes[], examples[]

  // A2A section
  a2a?: {
    endpoint: string // A2A JSON-RPC URL
    transport: 'https+json' | 'https+sse' | 'https+jsonrpc'
    auth: A2AAuth // none | bearer-jwt | oauth2
    capabilities?: {
      streaming?: boolean
      push_notifications?: boolean
      state_transition_history?: boolean
      extensions?: AgentExtension[] // declares spec extensions like PolicyPart v0.1
    }
  }

  // Optional ActivityPub section
  activitypub?: {
    actor_iri: string // matches WebFinger `self` href
    inbox: string
    outbox?: string
  }

  // Mentionable-specific section
  mentionable?: {
    signing_key: { alg: 'Ed25519'; kid: string; public_jwk: JsonWebKey }
    previous_keys?: Array<{ alg: string; kid: string; public_jwk: JsonWebKey }>
    identity_policy?: IdentityPolicy
  }
}

AgentCard.mentionable.signing_key

This Ed25519 key is load-bearing — it gates three sign/verify pairs:

  1. Agent self-sign attestations. When the agent issues identity evidence about itself or a delegation chain (urn:mentionable:auth:agent-self-sign:v0.1), it signs over canonical JSON with this key.
  2. Outbound PolicyPart wrapping. Where wire-level integrity is needed, the agent signs the PolicyPart payload.
  3. Receipt signing. When the agent issues PolicyResolution to itself across a callback boundary, this key authenticates the receipt.

Rotation: emit a new signing_key, list the previous one in previous_keys for a grace period (recommended: ≥30 days for cross-origin verifiers caching the card), then drop. Verifiers SHOULD accept any kid listed in either field.

Extensions in agent.a2a.capabilities

type AgentExtension = {
  uri: string // unique HTTPS URL
  description?: string
  required?: boolean
  params?: Record<string, unknown>
  endpoint?: string // REQUIRED for REST transport extension
}

Well-known extension URIs:

Connector Card

The Connector Card is the public discovery document for a Connector Instance (e.g. mentionable-slack.example). v0.1 path: /.well-known/adapter-card.

{
  "issuer": "mentionable-slack.example",
  "active_keys": [{ "alg": "Ed25519", "kid": "2026-05", "public_jwk": { ... } }],
  "previous_keys": [{ "alg": "Ed25519", "kid": "2026-04", "public_jwk": { ... } }],
  "callback_urls": {
    "consent": "https://mentionable-slack.example/consent",
    "payment": "https://mentionable-slack.example/payment/return"
  },
  "supported_methods": [
    "urn:mentionable:auth:slack-workspace-member:v0.1"
  ]
}

adapter-card is a v0.1 compatibility path. New documentation refers to the concept as “Connector Card” while the wire URL remains /.well-known/adapter-card until a future version replaces it.

Caching

ResourceDefault TTLMaximum TTL
WebFinger JRD1 hour24 hours
Agent Card1 hour (aggressive caching encouraged)24 hours
Connector Card1 hour24 hours
Agent’s signing_key.public_jwk1 hour with kid pinning24 hours

Cache by kid so signing-key rotation drains caches naturally. Receivers that pin a kid they have not seen before SHOULD revalidate the agent card.

basePath

When a Mentionable runtime is mounted under a path prefix (e.g. https://example.com/agents/...), the rule is:

This is mandatory because RFC 7033 mandates the apex path. The @mentionable/server TransportMount helper enforces this automatically — see deployment-patterns.

Common Mistakes