GitHub

Docs

PATH FINDER

Reachability in two steps — SONAR then RESOLVER — then a payee check, with the normative rules on budgets, answer shape and logging.

DRAFT — the layer is specified; the SONAR step is trivially satisfied while a single member holds an index.

The two steps

Fig. 01 — Two steps
  1. 01SonarWhich member holds this key?The network answers · ring 2
  2. 02ResolverWhat does this destination accept?The holder answers · ring 1
Finder = both, composed on the client

The Learn page explains why they stay apart. This page is the normative detail.

Composition is client-side

The two calls are chained by the caller, not by the network. A network MAY offer to relay step 2, and MUST declare it in its discovery document if it does.

A relaying network sees the destination's capability on top of every search: which rails a competitor supports, which assets, what limits, and how they change. That is not routing data. The default is therefore no relay, and the exception is published rather than silent.

Skipping step 1

Fig. 02 — Skipping step 1
ADirectorySonar + Resolver. Network membership required.
BRelationshipResolver alone. Nothing required.

/resolver/{key} MUST remain a route in its own right, callable without any credential. It is the only complete path that requires membership of no network, and every claim about the protocol's openness rests on it existing and being reachable.

Holder and gateway

DRAFT — the distinction is specified and the field is emitted. Answering as a gateway is RESERVED: PATH-FINDER.Gateway is a profile this implementation does not declare.

Every resolver answer carries standing, and it must be read before the terms are.

holdergateway
The destination isits own customerreachable, not held
The terms arethe destination'sthe intermediary's
commitmentpresentnull, structurally
The destination consentedyes, by its discoverabilityno — it does not know

commitment: null on a gateway answer is not a choice. A commitment is the receiver's pledge over its own terms, made when it set them. A receiver who does not know it is being addressed has pledged nothing, and there is nothing to quote.

A caller that cannot tell the two apart believes it read the receiver's terms when it read a middleman's — including the middleman's fee.

The index says who holds. The register says who reaches.

This is the load-bearing rule, and it decides where a gateway is found.

Directory indexMember register
Answerswhich member holds this key?which member reaches this rail?
Exposesother people's customersa member's own rail footprint
Already publicneveryes — it is on their website
Declared insonar_index, by registrationgateway in discovery

A gateway is therefore discovered by reading the register and choosing, then calling that member's resolver directly. That is the relationship profile — no directory, no membership, nothing to ask permission for.

A gateway MUST NOT appear in a SONAR answer.

An unknown key and a key that is known but not visible to the caller produce the same response, including on a range a member declares it can reach. Constant-shape answers admit no exception.

What a gateway answer will carry

RESERVED — described, not built.

{
  "standing": "gateway",
  "address": null,
  "accepts": [{ "rail": "pix", "asset_type": "fiat", "asset_code": "BRL" }],
  "limits": { "max_single": { "value": "20000", "asset_type": "fiat", "asset_code": "BRL" } },
  "fees": [{ "kind": "operator", "amount": { "value": "150", "asset_type": "fiat", "asset_code": "BRL" }, "paid_by": "payer" }],
  "expires_at": "2026-09-21T10:35:00Z",
  "commitment": null,
  "commitment_version": null
}

address is null: the key asked about is not an address this member issued, and there is no row behind it. fees because an intermediary takes a margin and a payer must see it before deciding — the same rule, and the same shape, as fee disclosure. expires_at because a gateway's terms are a momentary capacity, where a holder's standing intent is a permanent policy.

Holder confirmation does not apply

A gateway holds nothing, so it cannot co-sign "yes, mine". The question never arises: no gateway appears in a SONAR answer, so there is never a network claim to corroborate. And in the relationship profile there is no network in the exchange at all — the gateway's own signed answer is already the responding party speaking.

Holder confirmation means the destination is that member's. There is no second format.

Normative rules for SONAR

Constant-shape answers

An unknown key and a key that is known but not visible to the caller MUST produce identical responses.

Reciprocity budgets

Search allowances MUST be computed from what a member contributes to the index, not as a requests-per-second ceiling.

{
  "queries_per_registered_entry_per_month": 5,
  "absolute_floor": 1000
}

Budgets are sized from what a member contributes, not from a requests-per-second ceiling.

absolute_floor exists so that a member who has just joined and registered nothing is not stuck at zero.

Email takes its own budget.

Search allowance is bought with exposure, not with usefulness.

Search of other members' customers follows from exposing one's own to the same search. A member that registers nothing holds absolute_floor and no more. Reaching a rail does not raise this budget.

Logging

Every lookup MUST be recorded against the calling member, with the key, and MUST NOT record the identifier in the clear.

Retention MUST be declared in the discovery document. A credential without a log is a badge with no camera behind it.

Called server-side

SONAR MUST be called by a member's server, never from an end-user application. A credential shipped inside an app is public the moment someone opens the binary.

An application talks to its own backend, which talks to the directory. This is a real constraint on architecture, not a recommendation.

Batch

POST /sonar/lookup/batch is the contact-synchronisation shape. It MUST be capped explicitly rather than inheriting the general budget.

Look a key up when a payment is being prepared, not when an address book is first imported.

Holder confirmation

A network MAY require the holding member to co-sign, and declares this as holder_confirmation: required | optional | none. required is recommended.

Fig. 03 — Holder confirmation
  1. 01AskCaller asks Sonar“It’s at member-b.”
  2. 02ConfirmNetwork asks member-bMember-b signs: yes, mine, for this requester.
  3. 03ReturnCaller receives bothThe answer, plus the holder’s signature.

What it buys: the answer no longer rests on the network's word alone; the member gets a per-request veto under its own policy; and the index may be stale without the answer being wrong.

What it costs: a second round trip, the holder having to be up, and a shift in who observes — the holder now learns someone is looking for its customer, which it did not before.

The confirmation signs the key, member, requester, timestamp and nonce. Never the capability — routing that through the network hands it precisely what the split withholds. Without the requester and nonce, the confirmation is replayable.

Payee check

LIVE — specified in PIP-0013. This implementation serves it and declares PATH-FINDER.PayeeCheck.

SONAR says who holds a key. RESOLVER says what the destination accepts. Neither says whether the destination is the person the payer means to pay, and neither may: a routing answer never carries a name. The payee check is a third question, asked separately, when a payment is being prepared.

Fig. 04 — Payee check
  1. 01SonarWhich member holds this key?The network answers
  2. 02ResolverWhat does this destination accept?The holder answers
  3. 03Payee checkIs it the person I think?The holder answers, under its own policy
Asked of the holder, never of the network

Three questions can be asked about the subject behind a key. Only two are allowed.

QuestionExample
VerifyIs this Alice Martin? → matchAllowed
DisplayThe holder is Alice M.Allowed, masked
RevealBehind this key: Alice Martin, +33…, alice@…Never

No mode returns another identifier of the subject.

Rules

The holder answers. The name stays in the holding member's systems, as the subject requires. The network never learns it and never relays it.

A payment context is required. The caller presents a member credential, or the reference of a payment request or payment instruction issued for that address. A payee check is not a public read: the resolver is, and that is precisely why the name is not in it.

Two modes, one answer shape. verify takes the name the payer expects and returns match, close_match, no_match or unavailable; on close_match it adds the masked registered name. display returns the masked name alone. Masking is the holder's: by default the first given name and the initial of the family name for a natural person, the registered name for a legal entity.

The holder may decline. unavailable is a valid answer, whether the subject opted out or the jurisdiction forbids it. It is never an inference.

Nothing else is returned. No other identifier, no account details, no KYC level.

Signed, budgeted, logged. The answer is signed by the holder and binds the key, the requester, the mode, the result and the nonce. Calls are budgeted per requester and recorded with the key hashed, as for SONAR.

The same check against external networks — Pix, PI-SPI, SEPA Verification of Payee — is Crossway's payee check.

Network topologies

A network declares sonar_mode.

central — the network holds the index and answers. A member integrates in an afternoon, stores nothing, synchronises nothing, reads fresh data. The network sees the searches.

broadcast — the member asks its peers in parallel, narrowed by declared coverage.

"coverage": {
  "phone": ["+1"],
  "reg": ["US"],
  "bank_account": ["US", "GB"]
}

Coverage is what makes broadcast viable: a US number reaches only members serving the United States, cutting both traffic and exposure by an order of magnitude. It leaks nothing — "I serve the United States" is on the member's marketing page.

Three further rules for broadcast:

The member fans out, not the network. If the network relays, it sees every search and so do the peers — the drawbacks of both modes at once.

Negative caching belongs to the caller. What is worth remembering is "nobody holds this key", which is the common and expensive case, and remembering it caller-side avoids the trip rather than just the computation.

Silence is ambiguous. A member that does not answer may not hold the key, or may be down. A peer that is down otherwise becomes a peer that says no, and its customers become quietly unreachable. Pair it with an independent liveness signal.

Broadcast is not the more private option, and it is routinely assumed to be. Asking fifty members tells forty-nine of them that someone is looking for a person, when one needed to know. Central concentrates observation in a party bound by the rulebook; broadcast spreads it among competitors who are not.

Why there is no feed mode

Local search against a published index is not part of this design. The hashing secret stays with the network. See Privacy.


On this page