A2A Services

Connect to external agent services

A2A (Agent-to-Agent) services let a formation communicate with external agent systems. Put reusable service definitions in a2a/*.afs; configure inbound server auth and extended outbound auth in the top-level a2a: block.

Service files

Create a2a/analytics.afs:

schema: "1.0.0"
id: analytics-engine
name: Analytics Engine
description: External analytics service
url: "https://analytics.company.com"
active: true
timeout_seconds: 30
retry_attempts: 3

auth:
  type: bearer
  token: "${{ secrets.ANALYTICS_TOKEN }}"

Required fields are schema, id, name, description, and an HTTP(S) url. Optional top-level fields include active, timeout_seconds, retry_attempts, author, version, documentation, and support_contact. Service-file auth supports none, api_key, bearer, basic, and custom.

Top-level configuration

# formation.yaml
a2a:
  enabled: true
  inbound:
    enabled: true
    host: 0.0.0.0
    port: 8181
    auth:
      type: hmac
      secret: "${{ secrets.A2A_HMAC_SECRET }}"
      timestamp_tolerance: 300
  outbound:
    enabled: true
    default_timeout_seconds: 30
    default_retry_attempts: 3
    services:
      - id: analytics
        url: "https://analytics.example.com"
        auth:
          type: oauth2
          client_id: "analytics-client"
          client_secret: "${{ secrets.OAUTH_SECRET }}"
          token_url: "https://auth.example.com/oauth/token"
          scope: "read write"

id is the canonical identifier for credential registration. The runtime matches outbound requests through the service's url; when several URL paths match, the most specific path wins. The legacy service_id field remains accepted for compatibility but is deprecated.

Authentication

API key

auth:
  type: api_key
  header: X-API-Key
  key: "${{ secrets.SERVICE_API_KEY }}"

Bearer token

auth:
  type: bearer
  token: "${{ secrets.SERVICE_TOKEN }}"

Basic auth

auth:
  type: basic
  username: "${{ secrets.SERVICE_USERNAME }}"
  password: "${{ secrets.SERVICE_PASSWORD }}"

Custom headers

auth:
  type: custom
  headers:
    X-Tenant: acme
    X-Service-Key: "${{ secrets.SERVICE_KEY }}"

OAuth2 client credentials (outbound only)

a2a:
  outbound:
    services:
      - id: analytics
        url: "https://analytics.example.com"
        auth:
          type: oauth2
          client_id: "your-client-id"
          client_secret: "${{ secrets.OAUTH_SECRET }}"
          token_url: "https://auth.example.com/oauth/token"
          scope: "read write"

Tokens are cached until expiry. scope is an optional space-delimited string.

HMAC (inbound and outbound)

HMAC uses SHA-256 over the timestamp. Inbound verification uses constant-time comparison, rejects stale timestamps, and prevents replay. Defaults are X-Signature, X-Timestamp, and a 300-second inbound tolerance.

a2a:
  inbound:
    enabled: true
    auth:
      type: hmac
      secret: "${{ secrets.HMAC_SECRET }}"
      timestamp_tolerance: 300
  outbound:
    services:
      - id: analytics
        url: "https://analytics.example.com"
        auth:
          type: hmac
          secret: "${{ secrets.HMAC_SECRET }}"
          signature_header: X-Signature
          timestamp_header: X-Timestamp

OpenID Connect (inbound only)

OpenID verifies bearer JWTs against the issuer's JWKS and identifies callers as openid:. When jwks_url is omitted, the runtime uses /.well-known/jwks.json.

a2a:
  inbound:
    enabled: true
    auth:
      type: openid
      issuer: "https://auth.example.com"
      audience: "your-client-id"
      jwks_url: "https://auth.example.com/.well-known/jwks.json"
      allowed_algorithms: ["RS256"]
      clock_skew_seconds: 30

oauth2 is outbound-only and openid is inbound-only. api_key, bearer, basic, custom, and hmac are valid in both directions.

Learn More


Home Docs SDKs
Star on GitHub