Skip to content

v0.2.0

Latest

Choose a tag to compare

@github-actions github-actions released this 02 Nov 14:25
· 18 commits to main since this release

Agent Mesh v0.2.0 enables Ingress: a router can now expose a2a, mcp and inference services to HTTP clients outside the mesh, under the same identity and policy checks that apply inside it. This release also adds sdk/meshnet, a Go SDK that embeds a mesh member in a Go program, publishes the agentmesh MCP server to the MCP registry, and includes fixes to the node, the control plane, the Helm chart and the documentation.

Upgrade notes

  • v0.1.1 nodes, routers and control planes interoperate with v0.2.0. All API changes are additive.
  • The control plane database schema is now version 21. The migration is automatic and keeps existing data. Do not downgrade a migrated database to v0.1.1: the old binary does not know the ingress resource type and a policy save from it removes every allowed_ingress grant. Take a backup before upgrading.
  • agentmesh-one behind a proxy, including Cloud Run, requires --external-url. The URL is no longer taken from request headers (#755).
  • The Helm bootstrap Job seeds the mesh policy only when the control plane has none. A helm upgrade no longer replaces a policy edited in the console or with POST /policies (#763).
  • Ingress is off until configured. Roles have no allowed_ingress by default, services have no ingress: block, and agentmesh-router has no ingress listener unless --ingress-addr is set. agentmesh-one serves ingress on its existing port.

Ingress (#838, #839, #851)

Ingress exposes a service at a stable path on a router:

https://<router>/ingress/<peer_id>/<a2a|mcp|inference>/<name>[/<subpath>]

Two settings must both be present for a service to be reachable:

  1. The mesh policy grants it. PolicyRole.allowed_ingress lists service patterns and the permitted auth modes for the serving node's role. An empty list denies ingress for that role. A grant without allowed_auth_modes permits edge only.
  2. The node exposes it. The service in agentmesh-node.yaml has an ingress: block, with auth_mode (edge or passthrough) and an optional allow list in PolicyBinding.members syntax that narrows the callers.

The router checks the grant when the node announces the service and on every request. The node checks it again against its own credential before it hands the request to the backend.

edge mode (default). The router accepts a delegated Session Biscuit or a task Biscuit from POST /oauth/token. An unauthenticated request receives a 401 with WWW-Authenticate and RFC 9728 metadata at /.well-known/oauth-protected-resource. Before the router dials the provider it verifies the signature, expiry, revocation, bans, Block 0 policy, each tar_block and the service's allow list. Standing member and router transport Biscuits are refused as bearer credentials. The node verifies the caller's Biscuit again, removes caller-supplied X-Mesh-* headers and sets X-Mesh-Ingress, X-Mesh-Principal, X-Mesh-Role and X-Mesh-Task from the verified credential.

passthrough mode. The backend authenticates its own callers. The role must list INGRESS_AUTH_MODE_PASSTHROUGH explicitly. The control plane rejects passthrough on a service that also has target_auth_path or an inline credential.

A2A agent cards. GET /.well-known/agent-card.json on an ingress URL returns the backend's card with supportedInterfaces set to the public URL, signatures removed and, in edge mode, the mesh OAuth 2.1 security schemes.

Transport. Router and node communicate over /mesh/ingress/1.0.0. The node accepts this protocol only from a peer authenticated as mesh:role:router; other streams are reset before any data is read and counted in agentmesh_node_streams_refused_total{scope="not_router"}. Request bodies are limited to 32 MiB at the router and at the node. Authenticated callers are rate limited per Biscuit revocation ID. The router's revocation set is pruned by expiry instead of by size and is exported as agentmesh_router_revoked_tokens.

Router flags (all off by default):

Flag Purpose
--ingress-addr Address of the ingress listener
--ingress-url Public base URL, sent in the lease and published on /info as RouterInfo.ingress_url
--ingress-tls-cert-path, --ingress-tls-key-path TLS termination on the router
--public-control-plane Public control plane URL for RFC 9728 and A2A OAuth metadata
--allow-insecure-ingress-http Plain HTTP on a non-loopback address, for use behind a TLS-terminating gateway only

Deployment. The Helm chart and the .github/k8s manifests route /ingress/* on the Gateway HTTPRoute to the routers. Metrics: agentmesh_router_ingress_requests_total{mode,outcome} and agentmesh_node_external_ingress_requests_total{mode,outcome}. Documentation: External ingress.

Go SDK (#843)

github.com/google/agentmesh/sdk/meshnet embeds a mesh member in a Go program, in the style of tsnet.

  • Server.Up enrolls and attaches to routers.
  • ListenA2A serves an A2A agent to the mesh and, when policy allows, on router ingress.
  • HTTPClient and Discover call providers over authenticated streams.
  • WhoIs returns the verified caller of a request.
  • AttenuateToken appends a tar_block.
  • MCPServer and InferenceHandler expose the node's tools and the /v1 endpoint in process.

The SDK uses the same state directory format as agentmesh-node, sdk/js and sdk/python, and the same policy enforcement code as agentmesh-node. Documentation: Embedded Go SDK.

Control plane

  • Operator-managed signing keys: when rotating with --signing-key-path, keep the previous key in the file until every credential it signed has expired (24 hours by default). The flag help and the reference now state this (e8c06ff).
  • --key-rotation-interval 0 selects the default of 24 hours. --signing-key-path is what disables rotation (b54604e).
  • An enrollment refused because the identity has no binding for the requested role is logged with the peer, the role, the issuer, the subject and the roles the identity did resolve to (#756).
  • Border Crossing log lines at /sts/token include client_peer_id on allow and deny. act.sub in the border JWT is the egress node (#750, 95300a5).
  • --dht-replication help describes replication accurately: the router a member announces to keeps the record and copies it to this many closest routers. Members do not hold sessions with the replicas (5d321c4).

Node

  • Concurrent first calls to the same provider share one /mesh/auth handshake. Previously each call opened its own stream, the provider's limiter rejected the excess and the caller received a 502. Shared waits are counted as agentmesh_node_provider_reach_total{result="shared"}; a rate-limited handshake is reported as result="rate_limited" (#849).
  • A directory lookup that fails is retried on the next request. Previously the node cached its fallback (circuits through its own routers) for two minutes and did not ask the directory again in that time (#852).
  • A router that disconnects the node is redialed. A race between recording the session and clearing the redial mark could leave the router attached without a session; with a single router the node then exited (#725, #793).
  • A policy denial under the verifying key is final. The node no longer continues to other trusted keys after a denial. Initial key creation is serialized (#796).
  • An authorizer run limit is reported as such, not as a denial. The time limit returns 504 and CallMCPTool retries; the facts and iterations limits return 500 without retry. The authorizer error is preserved in the message (#842, 54ec3d3, #813).
  • /oauth/revoke accepts a Biscuit that verifies under a trusted key without a bearer token. Revocation records the last block's ID, so the parent token remains valid (45765f3).
  • The usage text for agentmesh-node join <control-plane-url> marks the argument as required (2c44e1e).

Router

  • Members hold no DHT or gossipsub streams on routers; only peer routers use /mesh/kad/1.0.0. The metrics documentation and the help of agentmesh_router_streams_refused_total are corrected (46757a6).

agentmesh-one

  • The advertised and signed public URL comes from --external-url or the local listener, never from Host, X-Forwarded-Host or X-Forwarded-Proto. A proxied request without --external-url is logged once and counted in agentmesh_one_proxied_requests_without_external_url_total (#755).

Helm chart

  • The bootstrap Job reads /admin/policy and seeds only when it holds no roles. A policy it cannot read fails the Job instead of being overwritten (#763).
  • PodDisruptionBudget for the control plane (#703).

SDKs and tools

  • @agentmesh-p2p/sdk includes an agentmesh executable: an MCP stdio server with the node's caller tools, configured through AGENTMESH_* variables. It is listed on the MCP registry as io.github.google/agentmesh and published by the release workflow (#837).
  • mcp-client pretty-prints JSON when stdout is a terminal (#835).
  • The console collapses the Revoked Nodes section like Expired Nodes (#834) and shows each router's ingress URL.

Documentation

The reference pages for authorization, enrollment, networking, metrics, APIs and CLI flags, and the getting-started guides were checked against the code and corrected (#760, #762, #764–#766, #782–#788). New reference pages: agentmesh-console and mcp-client. The site has search and heading anchors (#758).

New guides with runnable examples: Keycloak as identity provider (#753), agentgateway (#802) and kgateway (#812) in front of the mesh, Google APIs with a federated credential (#749), OpenShell sandboxes and Substrate actors on one mesh (#747, #754, #806), and a private DIY enterprise stack (#804).

Dependencies (#850)

go-libp2p v0.51.0, go-libp2p-kad-dht v0.43.0, go-libp2p-pubsub v0.19.0, quic-go v0.64.1, grpc v1.85.0, a2a-go v2.7.0, pgx v5.12.0, modernc.org/sqlite v1.61.1, golang.org/x/net v0.62.0, go-jose v4.1.6, envoy go-control-plane v1.40.0, prometheus/client_golang v1.25.0. Release images are built with Go 1.27.2.