FlexTrust liveness ladder — v1
Served at /spec/liveness. Published definitions for the liveness field every score answer, pre-check, passport and
agent page carries since 2026-10-27 (C2, ADR-019). A separate signal next to the score — never an input to it.
1 · What it answers
The score says what an agent has verifiably done. Liveness says whether the agent is reachable now: does its registration file resolve, does it declare an endpoint, does that endpoint answer, and does it speak the protocol it declares. A high score with a dead endpoint is a counterparty that was good. Readers are expected to look at both.
2 · The ladder
Every probe climbs the rungs in order and stops at the first that fails. The rung is the highest reached; below the
top, failure names the one reason it stopped.
| Rung | Name | Proven by | Stops with |
|---|---|---|---|
| 0 | registered |
an active ERC-8004 registration with no metadata_uri |
no_pointer |
| 1 | pointer |
a metadata_uri is present |
fetch_failed · ssrf_refused |
| 2 | resolves |
the file fetches: HTTP 2xx (or a data: / ipfs:// decode), ≤ 64 KB |
parse_failed |
| 3 | parses |
the file is a JSON object | no_endpoint |
| 4 | declares_endpoint |
≥ 1 parseable absolute http(s) URL in services[], endpoints[] or url on a public host (URL templates such as …/{agentId}/card are not endpoints) |
unreachable · no_answer · ssrf_refused |
| 5 | answers |
the endpoint returns HTTP 2xx, 401, 402, 403, 405 or 406 (404 / 410 / 5xx / silence do not count) | auth_required · no_protocol · handshake_failed |
| 6 | handshake |
the endpoint speaks its declared protocol (§3) | — |
alive = rung ≥ 5: the declared endpoint answers. Rung 6 proves the protocol, rung 5 proves life.
3 · Protocol handshakes (rung 6)
One endpoint per probe, chosen from the registration file by priority MCP > A2A > x402 > web/API; protocol
names the one probed. Plain web or API endpoints top out at rung 5 (no_protocol).
| Protocol | Request | Handshake when |
|---|---|---|
mcp |
POST JSON-RPC initialize (protocolVersion 2025-06-18), Accept: application/json, text/event-stream |
the result (JSON or one SSE data: frame) carries protocolVersion or serverInfo |
a2a |
GET the endpoint; if it is not a card and not a .json URL, GET <origin>/.well-known/agent-card.json |
a JSON card with name and url, skills or capabilities |
x402 |
GET the endpoint without payment |
HTTP 402 whose PAYMENT-REQUIRED header (base64 JSON) or JSON body carries accepts[] naming a network and a payTo |
web |
GET the endpoint |
never — no_protocol at rung 5 |
A 401 on any protocol is auth_required at rung 5: the endpoint answers, but the handshake cannot be proven from
outside.
4 · Safety rules of the probe
Agent files and endpoints are untrusted counterparty content. Every request goes through one outbound guard: http /
https only; the host is resolved once and the connection goes to that checked address; private, loopback,
link-local, carrier-grade NAT, zero, multicast and IPv4-mapped addresses are refused (ssrf_refused, never
attempted); redirects are followed at most twice and re-checked per hop; bodies are capped at 64 KB while streaming;
5 s timeouts; user agent flextrust-liveness. A probe sends no payment, no credentials and no agent data.
5 · Cadence and storage
- A sample of 1,024 registrations every 6 hours (never-probed first, then the stalest), over live registrations of scored agents on the priority chains (Base, BNB first); full passes run on demand.
- Every probe is one row of a time series — inserted, never overwritten: rung, failure, protocol, endpoint, HTTP
status, duration,
probed_at, ladder version. Rows older than 90 days are compacted to the registration’s latest row and every rung change, so history keeps its shape. - An agent’s
livenessis its best latest rung across its live registrations;probed_atis the newest probe.
6 · Where it is published
| Surface | Field |
|---|---|
GET /api/v1/scores/:identifier · MCP get_agent_score, explain_score |
liveness: { rung, name, alive, protocol, probed_at, ladder } or null |
GET /api/v1/precheck/:identifier · MCP check_counterparty |
same object |
Passport (/agents/:id/passport, /api/v1/passports/:identifier, MCP get_passport) |
credentialSubject.liveness, inside the signed subject (integers and strings only) |
Agent page /agents/:id |
the rung chip + JSON-LD PropertyValue |
/metrics and the landing page |
agents alive (rung ≥ 5) of agents probed, next to agents scored |
null means not yet probed — never a guess. The ladder version travels with every value (ladder: "v1"); a changed
definition is a new version, published here with a dated changelog, never a silent re-reading of old rows.
7 · What it is not
Not a score input, not a weighting, not a recommendation. The score stays evidence-only under the published
methodology; liveness is a measurement published next to it (ADR-019). It also does not prove who controls the
endpoint — that is the passport’s control continuity and the holder binding (bound).
Changelog
- v1 — 2026-10-27. First published ladder: rungs 0–6, protocol handshakes for MCP, A2A and x402, alive from rung 5.
- v1, clarified 2026-10-28. Rung 4 wording made explicit: an endpoint is a parseable absolute URL; URL templates (
{agentId}) never counted and now stop at rung 3no_endpointinstead of being recorded asssrf_refused(a labelling defect of the first day, rows re-probed). No definition change, no new version.