Delegated routing is a mechanism for IPFS implementations to use for offloading content routing, peer routing, and naming to another process/server. This specification describes a vendor-agnostic HTTP API for delegated content routing.
The Routing HTTP API uses the application/json
content type by default. For IPNS Names, the verifiable application/vnd.ipfs.ipns-record
content type is used.
As such, human-readable encodings of types are preferred. This specification may be updated in the future with a compact application/cbor
encoding, in which case compact encodings of the various types would be used.
0x72
) codec.Until required for business logic, servers should treat these types as opaque strings, and should preserve unknown JSON fields.
This API uses a standard version prefix in the path, such as /v1/...
. If a backwards-incompatible change must be made, then the version number should be increased.
GET /routing/v1/providers/{cid}
cid
is the CID to fetch provider records for.filter-addrs
(providers request query parameter)Optional ?filter-addrs
to apply Network Address Filtering from IPIP-484.
?filter-addrs=<comma-separated-list>
optional parameter that indicates which network transports to return by filtering the multiaddrs in the Addrs
field of the Peer schema.filter-addrs
parameter is a comma-separated (,
or %2C
) list of network transport protocol name strings as defined in the multiaddr protocol registry, e.g. ?filter-addrs=tls,webrtc-direct,webtransport
.unknown
can be be passed to include providers whose multiaddrs are unknown, e.g. ?filter-addrs=unknown
. This allows for not removing providers whose multiaddrs are unknown at the time of filtering (e.g. keeping DHT results that require additional peer lookup).!
, e.g. to skip IPv6 and QUIC addrs: ?filter-addrs=!ip6,!quic-v1
. Note that negative filtering is done by checking if the protocol name does not appear in any of the multiaddrs (logical AND).filter-protocols
(providers request query parameter)Optional ?filter-protocols
to apply IPFS Protocol Filtering from IPIP-484.
filter-protocols
parameter is a comma-separated (,
or %2C
) list of transfer protocol names, e.g. ?filter-protocols=unknown,transport-bitswap,transport-ipfs-gateway-http
.unknown
name can be be passed to include providers whose transfer protocol list is empty (unknown), e.g. ?filter-protocols=unknown
. This allows for including providers returned from the DHT that do not contain explicit transfer protocol information.Protocols
array (logical OR).filter-addrs
where only the multiaddrs that pass the filter are returned)200
(OK): the response body contains 0 or more records.404
(Not Found): must be returned if no matching records are found.422
(Unprocessable Entity): request does not conform to schema or semantic constraints.Content-Type
: the content type of this response, which MUST be application/json
or application/x-ndjson
(see streaming).Last-Modified
: an HTTP-date timestamp (RFC9110, Section 5.6.7) of the resolution, allowing HTTP proxies and CDNs to support inexpensive update checks via If-Modified-Since
Cache-Control: public, max-age={ttl}, public, stale-while-revalidate={max-ttl}, stale-if-error={max-ttl}
: meaningful cache TTL returned with the response.
max-age
SHOULD be shorter for responses whose resolution ended in no results (e.g. 15 seconds),
and longer for responses that have results (e.g. 5 minutes).max-ttl
, set to the maximum cache window of the underlying routing system.
For example, if Amino DHT results are returned, stale-while-revalidate
SHOULD be set to 172800
(48h, which at the time of writing this specification, is the provider record expiration window).Vary: Accept
: allows intermediate caches to play nicely with the different possible content types.{
"Providers": [
{
"Schema": "<schema>",
"ID": "bafz...",
"Addrs": ["/ip4/..."],
...
},
...
]
}
The application/json
responses SHOULD be limited to 100 providers.
The client SHOULD be able to make a request with Accept: application/x-ndjson
and get a stream with more results.
Each object in the Providers
list is a record conforming to a schema, usually the Peer Schema.
GET /routing/v1/peers/{peer-id}
peer-id
is the Peer ID to fetch peer records for,
represented as a CIDv1 encoded with libp2p-key
codec.filter-addrs
(peers request query parameter)Optional, same rules as filter-addrs
providers request query parameter.
filter-protocols
(peers request query parameter)Optional, same rules as filter-protocols
providers request query parameter.
200
(OK): the response body contains the peer record.404
(Not Found): must be returned if no matching records are found.422
(Unprocessable Entity): request does not conform to schema or semantic constraints.Content-Type
: the content type of this response, which MUST be application/json
or application/x-ndjson
(see streaming).Last-Modified
: an HTTP-date timestamp (RFC9110, Section 5.6.7) of the resolution, allowing HTTP proxies and CDNs to support inexpensive update checks via If-Modified-Since
Cache-Control: public, max-age={ttl}, public, stale-while-revalidate={max-ttl}, stale-if-error={max-ttl}
: meaningful cache TTL returned with the response.
ttl
SHOULD be shorter for responses whose resolution ended in no results (e.g. 15 seconds),
and longer for responses that have results (e.g. 5 minutes).max-ttl
, set to the maximum cache window of the underlying routing system.
For example, if Amino DHT results are returned, stale-while-revalidate
SHOULD be set to 172800
(48h, which at the time of writing this specification, is the provider record expiration window).Vary: Accept
: allows intermediate caches to play nicely with the different possible content types.{
"Peers": [
{
"Schema": "<schema>",
"Protocols": ["<protocol-a>", "<protocol-b>", ...],
"ID": "bafz...",
"Addrs": ["/ip4/..."],
...
},
...
]
}
The application/json
responses SHOULD be limited to 100 peers.
The client SHOULD be able to make a request with Accept: application/x-ndjson
and get a stream with more results.
Each object in the Peers
list is a record conforming to the Peer Schema.
GET /routing/v1/ipns/{name}
name
is the IPNS Name to resolve, encoded as CIDv1.200
(OK): the response body contains the IPNS Record for the given IPNS Name.404
(Not Found): must be returned if no matching records are found.406
(Not Acceptable): requested content type is missing or not supported. Error message returned in body should inform the user to retry with Accept: application/vnd.ipfs.ipns-record
.Etag
: a globally unique opaque string used for HTTP caching. MUST be derived from the protobuf record returned in the body.Cache-Control: public, max-age={ttl}, public, stale-while-revalidate={sig-ttl}, stale-if-error={sig-ttl}
: meaningful cache TTL returned with IPNS Record
max-age
value in seconds SHOULD match duration from IpnsEntry.data[TTL]
, if present and bigger than 0
. Otherwise, implementation SHOULD default to max-age=60
.sig-ttl
, set to the remaining number of seconds the returned IPNS Record is valid.Expires:
: an HTTP-date timestamp (RFC9110, Section 5.6.7) when the validity of IPNS Record expires (if ValidityType=0
, when signature expires)Last-Modified
: an HTTP-date timestamp of when cacheable resolution occured: allows HTTP proxies and CDNs to support inexpensive update checks via If-Modified-Since
Vary: Accept
: allows intermediate caches to play nicely with the different possible content types.The response body contains a IPNS Record serialized using the verifiable application/vnd.ipfs.ipns-record
protobuf format.
PUT /routing/v1/ipns/{name}
name
is the IPNS Name to publish, encoded as CIDv1.The content body must be a application/vnd.ipfs.ipns-record
serialized IPNS Record, with a valid signature matching the name
path parameter.
200
(OK): the provided IPNS Record was published.400
(Bad Request): the provided IPNS Record or IPNS Name are not valid.406
(Not Acceptable): submitted content type is not supported. Error message returned in body should inform the user to retry with Content-Type: application/vnd.ipfs.ipns-record
.This API does not support pagination, but optional pagination can be added in a backwards-compatible spec update.
JSON-based endpoints support streaming requests made
with Accept: application/x-ndjson
HTTP Header.
Steaming responses are formatted as Newline Delimited JSON (ndjson), with one result per line:
{"Schema": "<schema>", ...}
{"Schema": "<schema>", ...}
{"Schema": "<schema>", ...}
...
Streaming is opt-in and backwards-compatible with clients and servers that do not support streaming:
Accept: application/x-ndjson
header MUST default to
regular, non-streaming, JSON responses.application/json
response even
if the client requested streaming. It is up to the client to inspect
the Content-Type
header before parsing the response.400
(Bad Request): must be returned if an unknown path is requested.429
(Too Many Requests): may be returned along with optional Retry-After header to indicate to the caller that it is issuing requests too quickly.501
(Not Implemented): must be returned if a method/path is not supported.Browser interoperability requires implementations to support CORS.
JavaScript client running on a third-party Origin must be able to send HTTP request to the endpoints defined in this specification, and read the received values. This means HTTP server implementing this API must (1) support CORS preflight requests sent as HTTP OPTIONS, and (2) always respond with headers that remove CORS limits, allowing every site to query the API for results:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, OPTIONS
This section contains a non-exhaustive list of known schemas that MAY be supported by clients and servers.
The peer
schema represents an arbitrary peer.
{
"Schema": "peer",
"ID": "bafz...",
"Addrs": ["/ip4/..."],
"Protocols": ["transport-bitswap", ...]
...
}
ID
: the Peer ID as Multihash in Base58btc or CIDv1 with libp2p-key codec.Addrs
: an optional list of known multiaddrs for this peer.
ID
to lookup updated peer information.Protocols
: an optional list of protocols known to be supported by this peer.
ID
and Addrs
to lookup connect to the peer and use the libp2p identify protocol to learn about supported ones.To allow for protocol-specific fields and future-proofing, the parser MUST allow for unknown fields, and the clients MUST ignore unknown ones.
Below is an example on how one could include protocol-a
and protocol-b
protocols that includes an additional fields protocol-a
and protocol-b
.
If the client knows the protocol, they are free to use the extra binary (base64) or JSON information contained in the additional field. If that is not the case, the field MUST be ignored.
{
"Schema": "peer",
"ID": "bafz...",
"Addrs": ["/ip4/..."],
"Protocols": ["transport-bitswap", "protocol-a", "protocol-b", ...],
"protocol-a": "[base64-blob]",
"protocol-b": { "foo": "bar" }
}
Legacy schemas include ID
and optional Addrs
list just like
the peer
schema does.
These schemas are deprecated and SHOULD be replaced with peer
over time, but
MAY be returned by some legacy endpoints. In such case, a client MAY parse
them the same way as the peer
schema.
A legacy schema used by some routers to indicate a peer supports retrieval over
the /ipfs/bitswap[/*]
libp2p protocol.
{
"Protocol": "transport-bitswap",
"Schema": "bitswap",
"ID": "bafz...",
"Addrs": ["/ip4/..."]
}
A legacy schema used by some routers to indicate a peer supports retrieval over the graphsync libp2p protocol.
{
"Protocol": "transport-graphsync-filecoinv1",
"Schema": "graphsync-filecoinv1",
"ID": "bafz...",
"Addrs": ["/ip4/..."],
"PieceCID": "<cid>",
"VerifiedDeal": true,
"FastRetrieval": true
}
We gratefully acknowledge the following individuals for their valuable contributions, ranging from minor suggestions to major insights, which have shaped and improved this specification.