GitHub
Transverse

API reference

Tunnels

Recording, countersigning and revoking a tunnel; the gateway search; one search across networks.

PIP-0018. The calls on a tunnel are made by the network's operator, with its credential. The federated search is made by any member.


POST /tunnels

Records a tunnel agreed with another network and signs it for this one. Both networks send the same agreement; peer_kids are the keys the register lists for the other network.

{
  "reference": "tnl_8f2k4m9q",
  "networks": ["openconnect", "network-b"],
  "gates": {
    "openconnect": {
      "inbound": { "identifier_types": ["alias", "phone"], "zones": ["+221"], "queries_per_day": 50000 },
      "outbound": { "identifier_types": ["alias", "phone"] }
    },
    "network-b": {
      "inbound": { "identifier_types": ["alias"] },
      "outbound": { "identifier_types": ["alias", "phone"] }
    }
  },
  "gateways": {
    "openconnect": "https://gw.openconnect.example/api/path/v1",
    "network-b": "https://gw.network-b.example/api/path/v1"
  },
  "disputes": { "rulebook": "network-b" },
  "effective_at": "2026-11-01T00:00:00Z",
  "expires_at": "2027-11-01T00:00:00Z",
  "peer_kids": ["network-b_2026_01"]
}

The answer is the tunnel, pending, with this network's signature. Hand that signature to the other network.

POST /tunnels/{reference}/signatures

The other network's signature over the same tunnel.

{ "kid": "network-b_2026_01", "signed_at": "2026-10-09T10:00:00Z", "protocol_version": "0.2.0", "signature": "…" }

The key is read from the other network's domain — the origin of its gateway — at /.well-known/path-keys, and must be in peer_kids. The tunnel becomes active.

POST /tunnels/{reference}/revoke

Revokes the tunnel at once and publishes a signed revocation. No body.

GET /tunnels

Public. The network's active and revoked tunnels, each with status and signatures.

PUT /tunnels/exclusion

{ "excluded": true }

Keeps all of the calling member's subjects out of tunnels. Their entries then answer within the network only.


POST /tunnel/sonar

Called by the other network's gateway, signed with its key:

HeaderValue
Path-TunnelThe tunnel reference
Path-Key-IdA key listed for that network
Path-TimestampRFC 3339, within five minutes
Path-SignatureEd25519 over <method>\n<path>\n<timestamp>\n<sha256 of body>
{ "identifier_type": "phone", "identifier": "+221771234567", "nonce": "b1f7c2d9e4a6" }
{
  "kind": "tunnel_sonar_answer",
  "tunnel": "tnl_8f2k4m9q",
  "network": "openconnect",
  "requester_network": "network-b",
  "identifier_type": "phone",
  "identifier_hash": "…",
  "nonce": "b1f7c2d9e4a6",
  "found": true,
  "member": "member-a",
  "endpoint": "https://api.member-a.example",
  "protocol_version": "0.2.0",
  "signed_at": "…",
  "kid": "openconnect_2026_01",
  "signature": "…"
}

Only federated and public entries answer. Negatives are indistinguishable.

ErrorWhen
403 path.tunnel.inactiveThe tunnel is not in force
403 path.tunnel.not_admittedThis network's inbound gate does not admit the search
429 path.tunnel.budget_exhaustedThe gate's inbound budget is used for the day
401 path.tunnel.signature_invalidThe key is not listed, or the signature does not verify

POST /sonar/federated

Requires a member credential. One search, in this network and through every tunnel whose gates admit it.

{ "identifier_type": "phone", "identifier": "+221771234567" }
{
  "nonce": "4c1e…",
  "holders": [
    { "network": "openconnect", "member": "member-a", "endpoint": "https://api.member-a.example", "proof": { "…": "…" } },
    { "network": "network-b", "member": "member-x", "endpoint": "https://api.member-x.example", "proof": { "…": "…" } }
  ],
  "unavailable": []
}

holders lists every holder found, this network's first. Each proof is signed by the network that holds the entry. unavailable names the networks that did not answer in time. Show the network with each holder and let the payer choose; then call RESOLVER at the chosen holder.

On this page