GitHub

07 — PIPs · PIP-0018

Tunnels

A signed agreement between two PATH networks that lets a search cross from one directory to the other, within what each network's gate admits. Closed by default.

ImplementedNetworking
Number
PIP-0018
Status
implemented
Type
Networking
Author
PATH
Date
2026-10-09
Source
On the register

Why

A PATH network is a club: its directory answers its members (Networks and members). Several networks coexist, and a payer in one may need to reach a subject held in another. PATH CROSSWAY resolves destinations on networks outside PATH. This PIP defines the link between two PATH networks: the tunnel.

A member never deals with the other network. It asks its own network, which routes.

Terms

TermMeaning
**Tunnel**A signed agreement between two PATH networks. It exists only when both sign it
**Gate**What one network admits through a tunnel, in each direction. Each end of a tunnel has its own gate
**Gateway**The endpoint that relays searches for a network. A network may operate it or delegate it

A network with no tunnel takes no search from another network. That is the default.

The tunnel

{
  "type": "path.tunnel",
  "reference": "tnl_8f2k4m9q",
  "networks": ["openconnect", "network-b"],
  "gates": {
    "openconnect": {
      "inbound": { "identifier_types": ["alias", "phone"], "zones": ["+221", "+225"], "queries_per_day": 50000 },
      "outbound": { "identifier_types": ["alias", "phone"] }
    },
    "network-b": {
      "inbound": { "identifier_types": ["alias"], "queries_per_day": 20000 },
      "outbound": { "identifier_types": ["alias", "phone"] }
    }
  },
  "gateways": {
    "openconnect": "https://gw.openconnect.example/api/path/v1",
    "network-b": "https://gw.network-b.example/api/path/v1"
  },
  "rulebooks": {
    "openconnect": { "url": "https://…", "version": "1.2" },
    "network-b": { "url": "https://…", "version": "3.0" }
  },
  "disputes": { "rulebook": "network-b" },
  "effective_at": "2026-11-01T00:00:00Z",
  "expires_at": "2027-11-01T00:00:00Z",
  "status": "active",
  "signatures": [
    { "network": "network-b", "kid": "network-b_2026_01", "signed_at": "…", "protocol_version": "0.2.0", "signature": "…" },
    { "network": "openconnect", "kid": "openconnect_2026_01", "signed_at": "…", "protocol_version": "0.2.0", "signature": "…" }
  ]
}

The object is signed by both networks. Each signature covers the payload — every field except status, signatures and revocation — with its own protocol_version, signed_at and kid, as in any PATH envelope. networks is sorted, so both networks sign the same bytes. A tunnel is pending until it carries both signatures.

FieldRule
`networks`Exactly two network slugs
`gates.<network>.inbound`What that network answers for searches coming from the other. Absent: nothing
`gates.<network>.outbound`What that network sends to the other. Absent: nothing
`identifier_types`The types a gate admits. `address` is not among them: an address is resolved without a directory
`zones`Optional. A `phone` prefix (`+221`) restricts `phone`; a country code (`SN`) restricts `reg` and `tax`. Other types are not restricted by zone
`queries_per_day`Optional. The inbound budget of that network for this tunnel
`disputes.rulebook`The network whose rulebook governs disputes across the tunnel. Required
`status``active` or `revoked`

A search crosses only when the sending network's outbound gate and the receiving network's inbound gate both admit it.

Network keys

A network publishes its signing keys at /.well-known/path-keys on its own domain, in the same format as a member. The network register lists, for each network, its domain and the kid of its keys. A signature by a network is valid when its key is served on the network's domain and its kid is listed in the register. Rotation follows the rules for member keys.

Disputes

A tunnel names, under disputes, the rulebook that governs a dispute between a payer in one of its networks and a payee in the other. A tunnel without it is not valid.

"disputes": { "rulebook": "network-b" }

The value is one of the two networks. When the two networks agree on no other, they name the network of the payee. Procedures, time limits and liability are set by that rulebook.

Discoverability: `federated`

A new discoverability level, between network and public.

LevelFindable by
`private`Nobody, through the directory
`network`Members of the subject's network
`federated`Members of the subject's network, and of the networks its network has a tunnel with
`public`Anyone with access to a directory query

Only federated and public entries are answered through a tunnel. A subject chooses federated explicitly, through its member. A network may set federated as the default for alias in its rulebook, and states it in finder.aliases.default_discoverability; for every other type the defaults of PATH ID are unchanged.

A member may also exclude all of its subjects from tunnels. Its entries are then answered within its network only, whatever their level.

Resolution through a tunnel

SONAR answers which members hold this key. Within one network a key has at most one holder. Across networks it may have several: the same phone number can be held by a member of each. A search through tunnels returns every holder the gates admit.

  1. Fan-out. The payer's network runs SONAR on its own index and, at the same time, its gateway sends the search through every tunnel whose gates admit the identifier type and zone. An alias:<network>:<name> goes only to the network it names.
  2. SONAR in each other network. Its gateway normalises and hashes the identifier under its own pepper, and answers. The answer is signed by that network and names it. Negatives are indistinguishable, as within a network.
  3. One answer to the member. The payer's network returns every holder found, its own and those of the other networks, each with its network. The wallet shows the network with each result and the payer chooses.
  4. RESOLVER at the chosen holder. The payer's member calls the holding member directly, as in the relationship profile. No membership of the other network is required.
  5. Payee check, as in PIP-0013.

The inbound budget of each gate applies to every search it receives, whether or not it finds a holder.

Requests between gateways

A gateway signs each search it sends through a tunnel, as a member signs its requests:

HeaderValue
`Path-Tunnel`The tunnel `reference`
`Path-Key-Id`A `kid` of the sending network, listed for it in the register
`Path-Timestamp`RFC 3339, within five minutes
`Path-Signature`Ed25519 over `<method>\n<path>\n<timestamp>\n<sha256 of body>`

The receiving gateway answers only for a tunnel in force. Its answer is a signed envelope of kind tunnel_sonar_answer that binds the tunnel, both networks, the identifier type, the nonce and the result. The sending network checks each binding and the signature before it returns the answer.

What each party sees

PartySees
The payer's networkThe search, as within the network. It logs the requesting member
Each other network whose gates admit the searchThe identifier, in order to hash it under its own pepper; the requesting network and the tunnel. Not the requesting member
The holding memberThe RESOLVER call, as for any caller

Each network keeps its own search log, under its own declared retention. Each network's discovery document states its hashing mode, which applies to searches it receives through a tunnel.

What a gate does not close

A gate closes the directory. It does not close the protocol: a subject's PATH link or address can be read and paid by anyone who holds it (PATH INTEROP). A network that opens no tunnel keeps its directory to its members; it does not make its members' links unreachable.

Lifecycle

  • A tunnel is in force from effective_at until expires_at, or until revoked.
  • Either network revokes it alone. Revocation is signed, takes effect at once, and is published in the network register.
  • A change to a gate is a new tunnel, with a new reference, signed by both networks. The tunnel it replaces is revoked. A network that wants to narrow what it admits at once revokes the tunnel alone, then agrees the new one.

Publication

  • The network register lists each network's active tunnels and their gates.
  • A member's discovery document states, under finder.tunnels, the networks reachable through its network and the identifier types admitted.
"finder": {
  "tunnels": [
    { "network": "network-b", "identifier_types": ["alias", "phone"] }
  ]
}

Interface

CallWhoEffect
`POST /tunnels`The network's operatorRecords a tunnel agreed with another network, with `peer_kids`, and signs it. `pending`
`POST /tunnels/{reference}/signatures`The network's operatorAdds the other network's signature, verified against its keys. `active`
`POST /tunnels/{reference}/revoke`The network's operatorRevokes the tunnel and publishes a signed revocation
`GET /tunnels`AnyoneThe network's tunnels, as published
`PUT /tunnels/exclusion`A memberKeeps all of the member's subjects out of tunnels, or lets them back
`POST /tunnel/sonar`The other network's gatewayA search through the tunnel
`POST /sonar/federated`A memberOne search: this network and every admitting tunnel. Returns `holders` and `unavailable`

Errors: path.tunnel.inactive, path.tunnel.not_admitted, path.tunnel.budget_exhausted, path.tunnel.signature_invalid (Errors).

The SDK calls POST /sonar/federated with sonarFederated.

Conformance

A network that operates tunnels declares PATH-NETWORK.Tunnel: it signs and publishes its tunnels, applies its gates, answers only federated and public entries through them, and keeps negatives indistinguishable.

What this is not

  • Not CROSSWAY. CROSSWAY reaches registries outside PATH, through attested connectors. A tunnel links two PATH networks, under their two rulebooks.
  • Not a rail. No value moves through a tunnel. Payment executes on a rail both parties use.
  • Not a merger of directories. No network holds another's index. Each answers for its own members.

Compatibility

Additive. federated is a new value of discoverability; a reader that does not know it treats the entry as network. Networks without tunnels are unaffected.