Docs
PATH ADDRESS
Opaque, portable addresses; what they point at; and the standing terms on which they receive.
LIVE — addresses, standing intent, published
commitment and receive targets. Inbound states stay
RESERVED — PATH-ADDR.Inbound is a profile
this implementation does not declare.
Path-Version: 2026-10-04.genesis writes capabilities, per-capability limits
and receive targets (PIP-0008), identifiers from the rail register,
and payment instructions (PIP-0009). A caller pinned to 2026-09-09.genesis writes
the 0.3 bodies, signed protocol_version 0.1.0. Signed 0.3 objects are served as signed.
The address
Nothing in it identifies a member, a network, a country or an institution. That is a deliberate trade, and both sides of it are worth stating.
What it buys: portability. A person changes provider — or changes network — and the address follows, because no part of it ever belonged to the institution. This is the strictest reading of "the alias belongs to the person".
What it costs: reachability. Something has to say where to look, and that something is PATH SONAR. An address alone tells a stranger nothing.
The alternative — encoding the member in the address, alice@member.example — makes lookups trivial
and makes leaving expensive. Every saved destination breaks on the day someone switches provider,
which is precisely the lock-in the protocol exists to remove.
Structural rule
An address is attached to a subject, never to an account. An address bound to a wallet dies with the wallet, and the portability above becomes a slogan. In the reference schema there is no foreign key from an address to any account for exactly this reason.
Rotation
An address can be rotated without changing the underlying identity. The new one is issued, the old one is revoked rather than deleted, and the link between them is kept.
That last point is not bookkeeping. A payer holding a saved destination deserves "this moved" rather than "this never existed" — the second reads as an error on the payer's side, and they will call support about it.
Two profiles
| Profile | How a stranger reaches it | Requires |
|---|---|---|
directory | Via a network's index | Network membership |
relationship | The counterparty already knows where to ask | Nothing |
relationship is not a lesser mode. It is the complete path from key to capability that needs
permission from nobody, and it is what makes the protocol's openness checkable rather than asserted.
A platform and its payment provider, two institutions with an agreement — they use it and join
nothing.
Receive targets
LIVEWhat an address actually points at inside the member. Created at
POST /addresses/{id}/targets. The resolver reads them; account_ref never leaves the member.
{
"targets": [
{
"grammar": 2,
"rail": "blockchain",
"chain": { "slug": "base", "caip2": "eip155:8453", "evm": { "chain_id": 8453 } },
"asset_type": "token",
"asset_code": "USDC",
"standard": "erc20",
"contract": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"account_kind": "wallet",
"beneficiary_format": "chain_address",
"beneficiary": "0x1111111111111111111111111111111111111111",
"is_default": true
},
{
"grammar": 2,
"rail": "book",
"asset_type": "fiat",
"asset_code": "USD",
"account_kind": "own_ledger",
"beneficiary_format": "path_address",
"is_default": false
}
]
}A target serves one capability. account_kind says what kind of account it
is. beneficiary is stored only for a chain address, which is public by nature. A book target
has none: its destination is the PATH address. account_ref never leaves the member.
Targets never leave the member. What a caller sees is the capability summary the resolver returns — what can be sent, within what limits — and never the account identifiers behind it.
Standing intent
LIVEThe permanent terms on which an address receives.
{
"version": 3,
"grammar": 2,
"accepts": [
{
"rail": "blockchain",
"chain": { "slug": "base" },
"asset_type": "token",
"asset_code": "USDC",
"standard": "erc20",
"contract": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
},
{
"rail": "book",
"asset_type": "fiat",
"asset_code": "USD",
"limits": { "max_single": { "value": "500000", "asset_type": "fiat", "asset_code": "USD" } }
}
]
}Versioned, never overwritten. A payer may have read version 3 an hour ago and be sending against it now. Overwriting makes it impossible to establish afterwards which terms were actually in force — which is exactly the question a dispute turns on.
Policy beats capability. A member may hold a USDC wallet and still decline USDC this month. The standing intent is the receiver's own statement and wins over what the raw targets imply.
Setting it is what produces the commitment. The digest over {grammar, address, accepts, limits} is
computed here, at the moment the terms are pledged, stored against this version, and published at
GET /commitments/{address}. The resolver quotes it; it never recomputes it. An address with no
standing intent has no commitment, and the resolver says so with null — see
the limits of that guarantee.
policy.unknown_sender is accepted and stored, and nothing acts on it yet: it is not returned by the
resolver and there is no inbound machinery to enforce it. It is recorded for the version that has
one.
A standing intent is a policy — I accept USDC. A payment request is a claim — you owe me 5,000 for order 123. Different speech acts, different pillars. Merging them is how a system ends up unable to expire one or revoke the other.
Inbound states
RESERVED — described, not built. No state
column, no transition, no endpoint; PATH-ADDR.Inbound exists as a profile precisely so that an
operator who has none of this does not get to claim it under another name.
QUARANTINED is the state that has to exist, and the reason is specific to open rails: on-chain,
you cannot decline to receive. If tainted funds arrive, the beneficiary already holds them by the
time anyone notices. Funds must land in escrow, be screened, and only then trigger conversion —
never the other way round.
Two things a specification should say and usually does not: RETURNED is an attempt, not a
guarantee — the source may be an exchange hot wallet, a contract with no receive function, or a
pooled address. And FROZEN is the terminal state when returning is impossible or legally
forbidden, which is a real outcome and better named than improvised.
Resolving
What a caller gets back:
{
"standing": "holder",
"valid": true,
"grammar": 2,
"accepts": [
{
"rail": "blockchain",
"chain": { "slug": "base", "caip2": "eip155:8453", "evm": { "chain_id": 8453 } },
"asset_type": "token",
"asset_code": "USDC",
"standard": "erc20",
"contract": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
}
],
"limits": {},
"commitment": "8f29…",
"commitment_version": 3,
"protocol_version": "0.2.0",
"signed_at": "2026-09-09T10:00:00Z",
"kid": "op_example_2026_01",
"signature": "…"
}Capability and limits. Never the holder's name, institution, KYC tier or balance. A routing question gets a routing answer — anything more turns a lookup into a disclosure, and the whole privacy model rests on that not happening.
A revoked address answers revoked, not unknown. The payer needs the difference: one means
try another route, the other means you have the wrong destination.
standing says in what capacity the answer is given — holder here, meaning these are the
destination's own terms and the commitment above is its own pledge. Read it before you read the
terms: a gateway answer carries an intermediary's terms and no
commitment at all, and the two are otherwise the same shape.