From 0a3d8e3e01aa4a1760917b127b8d6079d74ead2d Mon Sep 17 00:00:00 2001 From: achingbrain Date: Thu, 6 Jun 2024 08:19:55 +0100 Subject: [PATCH 01/12] http-routing: add "get closest peers" operation Adds a new HTTP endpoint that can be used to request records for the closest peers to a given key that the routing implementation knows about. The use-case for this is browser nodes performing random walks to find peers that they can make a circuit relay reservation on, without having to be DHT clients to perform the walk which can be undesirable given all the connection/processing overhead that entails. --- src/routing/http-routing-v1.md | 54 ++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/routing/http-routing-v1.md b/src/routing/http-routing-v1.md index edd30cea3..350c68e31 100644 --- a/src/routing/http-routing-v1.md +++ b/src/routing/http-routing-v1.md @@ -106,6 +106,60 @@ Each object in the `Providers` list is a record conforming to a schema, usually ## Peer Routing API +### `GET /routing/v1/closest-peers/{peer-id}?[closerThan]&[count]` + +#### Path Parameters + +- `peer-id` is a [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md) represented as a CIDv1 encoded with `libp2p-key` codec. + +#### Query Paramters + +- `closerThan` is an optional [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md) represented as a CIDv1 encoded with `libp2p-key` codec. + - Returned peer records must be closer to `peer-id` than `closerThan`. + - If omitted the routing implementation should use it's own [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md). +- `count` is an optional number that specifies how many peer records the requester desires. + - Minimum 1, maximum 100, default 20. + +#### Response Status Codes + +- `200` (OK): the response body contains peer 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. + +#### Response Headers + +- `Content-Type`: the content type of this response, which MUST be `application/json` or `application/x-ndjson` (see [streaming](#streaming)). +- `Last-Modified`: an HTTP-date timestamp ([RFC9110, Section 5.6.7](https://www.rfc-editor.org/rfc/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. + - When present, `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). + - Implementations SHOULD include `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. + +#### Response Body + +```json +{ + "Peers": [ + { + "Schema": "", + "Protocols": ["", "", ...], + "ID": "bafz...", + "Addrs": ["/ip4/..."], + ... + }, + ... + ] +} +``` + +The number of peer records in the responses SHOULD be limited to the `count` query parameter, which defaults to 20 if unspecified. + +The client SHOULD be able to make a request with `Accept: application/x-ndjson` and get a [stream](#streaming) with more results. + +Each object in the `Peers` list is a record conforming to the [Peer Schema](#peer-schema). + ### `GET /routing/v1/peers/{peer-id}` #### Path Parameters From 351b8a7b5078e282e491d5508d3e29bf4b466531 Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Tue, 13 Aug 2024 10:53:53 +0100 Subject: [PATCH 02/12] docs: apply suggestions from code review Co-authored-by: Guillaume Michel --- src/routing/http-routing-v1.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routing/http-routing-v1.md b/src/routing/http-routing-v1.md index 350c68e31..d8b01d61f 100644 --- a/src/routing/http-routing-v1.md +++ b/src/routing/http-routing-v1.md @@ -116,7 +116,7 @@ Each object in the `Providers` list is a record conforming to a schema, usually - `closerThan` is an optional [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md) represented as a CIDv1 encoded with `libp2p-key` codec. - Returned peer records must be closer to `peer-id` than `closerThan`. - - If omitted the routing implementation should use it's own [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md). + - If omitted the routing implementation should use its own [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md). - `count` is an optional number that specifies how many peer records the requester desires. - Minimum 1, maximum 100, default 20. @@ -134,7 +134,7 @@ Each object in the `Providers` list is a record conforming to a schema, usually - When present, `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). - Implementations SHOULD include `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). + For example, if Amino DHT results are returned, `stale-while-revalidate` SHOULD be set to `79200` (22h, which at the time of writing this specification, is the [Provider Record Republish Interval](https://github.com/libp2p/specs/tree/master/kad-dht#content-provider-advertisement-and-discovery)). - `Vary: Accept`: allows intermediate caches to play nicely with the different possible content types. #### Response Body From 9aa55a3c6880fe1a42c39e67c788549467d3a642 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 19 Aug 2025 19:21:13 +0200 Subject: [PATCH 03/12] fix: use agreed path for closest peers endpoint update endpoint path to /routing/v1/dht/closest/{peer-id} as agreed in PR review comments --- src/routing/http-routing-v1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routing/http-routing-v1.md b/src/routing/http-routing-v1.md index d8b01d61f..ea4098395 100644 --- a/src/routing/http-routing-v1.md +++ b/src/routing/http-routing-v1.md @@ -106,7 +106,7 @@ Each object in the `Providers` list is a record conforming to a schema, usually ## Peer Routing API -### `GET /routing/v1/closest-peers/{peer-id}?[closerThan]&[count]` +### `GET /routing/v1/dht/closest/{peer-id}?[closerThan]&[count]` #### Path Parameters From d4e94a85d058e0ff2bafd50b1753dd0d7977fea1 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 19 Aug 2025 19:42:16 +0200 Subject: [PATCH 04/12] refactor: move DHT endpoint to separate section - create new "DHT Routing API" section for DHT-specific operations - move /routing/v1/dht/closest/{peer-id} to the new DHT section - keep general peer lookup in "Peer Routing API" section - update cache value to 172800 (48h) for consistency - fix typo: "Query Paramters" -> "Query Parameters" --- src/routing/http-routing-v1.md | 38 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/routing/http-routing-v1.md b/src/routing/http-routing-v1.md index ea4098395..f128142a3 100644 --- a/src/routing/http-routing-v1.md +++ b/src/routing/http-routing-v1.md @@ -106,23 +106,16 @@ Each object in the `Providers` list is a record conforming to a schema, usually ## Peer Routing API -### `GET /routing/v1/dht/closest/{peer-id}?[closerThan]&[count]` +### `GET /routing/v1/peers/{peer-id}` #### Path Parameters -- `peer-id` is a [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md) represented as a CIDv1 encoded with `libp2p-key` codec. - -#### Query Paramters - -- `closerThan` is an optional [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md) represented as a CIDv1 encoded with `libp2p-key` codec. - - Returned peer records must be closer to `peer-id` than `closerThan`. - - If omitted the routing implementation should use its own [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md). -- `count` is an optional number that specifies how many peer records the requester desires. - - Minimum 1, maximum 100, default 20. +- `peer-id` is the [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md) to fetch peer records for, +represented as a CIDv1 encoded with `libp2p-key` codec. #### Response Status Codes -- `200` (OK): the response body contains peer records. +- `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. @@ -134,7 +127,7 @@ Each object in the `Providers` list is a record conforming to a schema, usually - When present, `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). - Implementations SHOULD include `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 `79200` (22h, which at the time of writing this specification, is the [Provider Record Republish Interval](https://github.com/libp2p/specs/tree/master/kad-dht#content-provider-advertisement-and-discovery)). + 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. #### Response Body @@ -154,22 +147,31 @@ Each object in the `Providers` list is a record conforming to a schema, usually } ``` -The number of peer records in the responses SHOULD be limited to the `count` query parameter, which defaults to 20 if unspecified. +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](#streaming) with more results. Each object in the `Peers` list is a record conforming to the [Peer Schema](#peer-schema). -### `GET /routing/v1/peers/{peer-id}` +## DHT Routing API + +### `GET /routing/v1/dht/closest/{peer-id}?[closerThan]&[count]` #### Path Parameters -- `peer-id` is the [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md) to fetch peer records for, -represented as a CIDv1 encoded with `libp2p-key` codec. +- `peer-id` is a [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md) represented as a CIDv1 encoded with `libp2p-key` codec. + +#### Query Parameters + +- `closerThan` is an optional [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md) represented as a CIDv1 encoded with `libp2p-key` codec. + - Returned peer records must be closer to `peer-id` than `closerThan`. + - If omitted the routing implementation should use its own [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md). +- `count` is an optional number that specifies how many peer records the requester desires. + - Minimum 1, maximum 100, default 20. #### Response Status Codes -- `200` (OK): the response body contains the peer record. +- `200` (OK): the response body contains peer 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. @@ -201,7 +203,7 @@ represented as a CIDv1 encoded with `libp2p-key` codec. } ``` -The `application/json` responses SHOULD be limited to 100 peers. +The number of peer records in the responses SHOULD be limited to the `count` query parameter, which defaults to 20 if unspecified. The client SHOULD be able to make a request with `Accept: application/x-ndjson` and get a [stream](#streaming) with more results. From ab45d970cdd697b6e0e5045a0313b1c8f1825d1a Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 19 Aug 2025 19:49:58 +0200 Subject: [PATCH 05/12] feat: future-proof DHT API with /peers/ segment rename /routing/v1/dht/closest/{peer-id} to /routing/v1/dht/closest/peers/{peer-id} for future-proofing, as we may add API for querying entire keyspace in the future also update document date to 2025-08-19 --- src/routing/http-routing-v1.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routing/http-routing-v1.md b/src/routing/http-routing-v1.md index f128142a3..4fd0f8e9d 100644 --- a/src/routing/http-routing-v1.md +++ b/src/routing/http-routing-v1.md @@ -4,7 +4,7 @@ description: > 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 an HTTP API for delegated routing of content, peers, and IPNS. -date: 2024-03-22 +date: 2025-08-19 maturity: reliable editors: - name: Gus Eggert @@ -155,7 +155,7 @@ Each object in the `Peers` list is a record conforming to the [Peer Schema](#pee ## DHT Routing API -### `GET /routing/v1/dht/closest/{peer-id}?[closerThan]&[count]` +### `GET /routing/v1/dht/closest/peers/{peer-id}?[closerThan]&[count]` #### Path Parameters From 7c79e02912c80cda31a13a89620bf6d1a9d508bc Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 19 Aug 2025 20:15:01 +0200 Subject: [PATCH 06/12] feat: add IPIP-0476 for DHT closest peers API documents the new /routing/v1/dht/closest/peers/{peer-id} endpoint that enables lightweight peer discovery for browser nodes and other resource-constrained clients without requiring full DHT participation --- src/ipips/ipip-0476.md | 145 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 src/ipips/ipip-0476.md diff --git a/src/ipips/ipip-0476.md b/src/ipips/ipip-0476.md new file mode 100644 index 000000000..9ac4c1e51 --- /dev/null +++ b/src/ipips/ipip-0476.md @@ -0,0 +1,145 @@ +--- +title: "IPIP-0476: Delegated Routing DHT Closest Peers API" +date: 2025-08-19 +ipip: proposal +editors: + - name: Alex Potsides + github: achingbrain + affiliation: + name: Shipyard + url: https://ipshipyard.com + - name: Marcin Rataj + github: lidel + affiliation: + name: Shipyard + url: https://ipshipyard.com +relatedIssues: + - https://github.com/ipfs/specs/pull/476 + - https://github.com/ipfs/specs/pull/497 +order: 476 +tags: ['ipips'] +--- + +## Summary + +Add a new HTTP endpoint to the Delegated Routing API that allows clients to find the closest peers to a given peer ID without being full DHT clients. + +## Motivation + +Browser nodes and other resource-constrained clients need to perform peer discovery operations without the overhead of being full DHT clients. The primary use case is for browser nodes performing random walks to find peers that they can make circuit relay reservations on. + +Currently, to find peers close to a particular key in the DHT keyspace, a node must: +1. Be a full DHT client with all the associated overhead +2. Maintain connections to many peers +3. Handle the complexity of the DHT protocol + +This is particularly problematic for: +- Browser nodes that need to find circuit relay servers +- Light clients that want to populate their routing tables +- Applications that need to find peers to host provider records + +## Detailed design + +This IPIP introduces a new "DHT Routing API" section to the Delegated Routing V1 HTTP API specification with the following endpoint: + +### `GET /routing/v1/dht/closest/peers/{peer-id}` + +#### Path Parameters + +- `peer-id`: The target peer ID to find closest peers for, represented as a CIDv1 encoded with `libp2p-key` codec + +#### Query Parameters + +- `closerThan` (optional): A peer ID represented as a CIDv1 encoded with `libp2p-key` codec + - Returned peer records must be closer to `peer-id` than `closerThan` + - If omitted, the routing implementation should use its own peer ID +- `count` (optional): Number of peer records to return + - Minimum 1, maximum 100, default 20 + +#### Response + +The response follows the same format as the existing peer routing endpoints, returning a JSON object with a `Peers` array containing peer records conforming to the Peer Schema. + +### Specification Changes + +The following changes are made to `src/routing/http-routing-v1.md`: + +1. Add a new "## DHT Routing API" section after the "Peer Routing API" section +2. Document the `/routing/v1/dht/closest/peers/{peer-id}` endpoint with its parameters and response format +3. Update the document date to reflect the modification + +## Design rationale + +The design follows several key principles: + +### Simple MVP Approach + +The endpoint provides the minimum viable functionality needed for the primary use cases. It can be expanded later if more complex routing scenarios emerge. + +### Future-Proofed Path Structure + +The path `/routing/v1/dht/closest/peers/{peer-id}` is intentionally structured to allow future expansion: +- The `/dht/` segment clearly indicates DHT-specific operations +- The `/closest/peers/` structure allows for potential future endpoints like `/closest/keys/` for keyspace queries +- This organization keeps the API logical and extensible + +### Routing-Agnostic Implementation + +While the endpoint is in the DHT namespace, implementations have flexibility in how they determine "closest" peers. This allows for optimization based on the specific routing system being used. + +### Consistency with Existing API + +The endpoint follows the established patterns of the Delegated Routing API: +- Uses the same response format as other peer routing endpoints +- Follows the same parameter conventions +- Maintains consistency in error handling and status codes + +### User benefit + +This enhancement provides significant benefits to end users: + +1. **Browser Compatibility**: Browser nodes can discover circuit relay servers without implementing the full DHT protocol +2. **Reduced Resource Usage**: Light clients save bandwidth and processing power by delegating peer discovery +3. **Faster Peer Discovery**: Delegated routing servers can provide cached results more quickly than performing DHT walks +4. **Simplified Implementation**: Application developers can implement peer discovery with simple HTTP requests instead of complex DHT logic + +### Compatibility + +This change is fully backward compatible: +- It adds a new endpoint without modifying existing ones +- Existing clients continue to work unchanged +- Servers that don't implement the endpoint return 501 (Not Implemented) as per the specification + +### Security + +The new endpoint introduces no additional security considerations beyond those already present in the Delegated Routing API: + +- Standard rate limiting should be applied to prevent abuse +- The endpoint reveals no more information than a DHT query would +- Access controls can be implemented at the HTTP layer if needed +- Response caching helps mitigate potential DoS attacks + +### Alternatives + +Several alternatives were considered: + +1. **Full DHT Client Implementation**: Rejected due to excessive resource requirements for browser and mobile environments + +2. **Custom libp2p Protocol**: Would require all nodes to implement a new protocol, creating adoption barriers + +3. **Extension of Existing Peer Routing**: The `/routing/v1/peers/` endpoint serves a different purpose (finding specific peers rather than closest peers) + +4. **Amino-Specific Endpoint**: Initially considered `/routing/v1/amino/` namespace but rejected in favor of the more generic `/dht/` approach + +## Test fixtures + +This IPIP does not deal with content-addressed data, so specific test CIDs are not applicable. However, implementations should test: + +1. Valid peer ID inputs return appropriate closest peers +2. The `closerThan` parameter correctly filters results +3. The `count` parameter limits results as specified +4. Invalid peer IDs return appropriate error responses + +### Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). \ No newline at end of file From 4bdfa84dd93621377f122427d477f80123c6947e Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 19 Aug 2025 20:29:24 +0200 Subject: [PATCH 07/12] refactor: move DHT Routing API after IPNS API reorganize sections for better logical flow: - Content Routing API - Peer Routing API - IPNS API - DHT Routing API (moved here) --- src/routing/http-routing-v1.md | 88 +++++++++++++++++----------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/routing/http-routing-v1.md b/src/routing/http-routing-v1.md index 5896cadd6..1631b29a6 100644 --- a/src/routing/http-routing-v1.md +++ b/src/routing/http-routing-v1.md @@ -200,6 +200,50 @@ The client SHOULD be able to make a request with `Accept: application/x-ndjson` Each object in the `Peers` list is a record conforming to the [Peer Schema](#peer-schema). +## IPNS API + +### `GET /routing/v1/ipns/{name}` + +#### Path Parameters + +- `name` is the :ref[IPNS Name] to resolve, encoded as CIDv1. + +#### Response Status Codes + +- `200` (OK): the response body contains the :ref[IPNS Record] for the given :ref[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`. + +#### Response Headers + +- `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 :ref[IPNS Record] + - The `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`. + - Implementations SHOULD include `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](https://www.rfc-editor.org/rfc/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 occurred: 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. + +#### Response Body + +The response body contains a :ref[IPNS Record] serialized using the verifiable [`application/vnd.ipfs.ipns-record`](https://www.iana.org/assignments/media-types/application/vnd.ipfs.ipns-record) protobuf format. + +### `PUT /routing/v1/ipns/{name}` + +#### Path Parameters + +- `name` is the :ref[IPNS Name] to publish, encoded as CIDv1. + +#### Request Body + +The content body must be a [`application/vnd.ipfs.ipns-record`][application/vnd.ipfs.ipns-record] serialized :ref[IPNS Record], with a valid signature matching the `name` path parameter. + +#### Response Status Codes + +- `200` (OK): the provided :ref[IPNS Record] was published. +- `400` (Bad Request): the provided :ref[IPNS Record] or :ref[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`. + ## DHT Routing API ### `GET /routing/v1/dht/closest/peers/{peer-id}?[closerThan]&[count]` @@ -256,50 +300,6 @@ The client SHOULD be able to make a request with `Accept: application/x-ndjson` Each object in the `Peers` list is a record conforming to the [Peer Schema](#peer-schema). -## IPNS API - -### `GET /routing/v1/ipns/{name}` - -#### Path Parameters - -- `name` is the :ref[IPNS Name] to resolve, encoded as CIDv1. - -#### Response Status Codes - -- `200` (OK): the response body contains the :ref[IPNS Record] for the given :ref[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`. - -#### Response Headers - -- `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 :ref[IPNS Record] - - The `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`. - - Implementations SHOULD include `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](https://www.rfc-editor.org/rfc/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 occurred: 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. - -#### Response Body - -The response body contains a :ref[IPNS Record] serialized using the verifiable [`application/vnd.ipfs.ipns-record`](https://www.iana.org/assignments/media-types/application/vnd.ipfs.ipns-record) protobuf format. - -### `PUT /routing/v1/ipns/{name}` - -#### Path Parameters - -- `name` is the :ref[IPNS Name] to publish, encoded as CIDv1. - -#### Request Body - -The content body must be a [`application/vnd.ipfs.ipns-record`][application/vnd.ipfs.ipns-record] serialized :ref[IPNS Record], with a valid signature matching the `name` path parameter. - -#### Response Status Codes - -- `200` (OK): the provided :ref[IPNS Record] was published. -- `400` (Bad Request): the provided :ref[IPNS Record] or :ref[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`. - ## Pagination This API does not support pagination, but optional pagination can be added in a backwards-compatible spec update. From dd86eb35aa555e6a39d0df795a50a6b4b28d8c7d Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Sat, 13 Sep 2025 01:47:14 +0200 Subject: [PATCH 08/12] fix: use kebab-case for query parameter naming ref: https://github.com/ipfs/specs/pull/476#discussion_r2313723632 Co-authored-by: Hector Sanjuan --- src/ipips/ipip-0476.md | 4 ++-- src/routing/http-routing-v1.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ipips/ipip-0476.md b/src/ipips/ipip-0476.md index 9ac4c1e51..6447fd68c 100644 --- a/src/ipips/ipip-0476.md +++ b/src/ipips/ipip-0476.md @@ -50,8 +50,8 @@ This IPIP introduces a new "DHT Routing API" section to the Delegated Routing V1 #### Query Parameters -- `closerThan` (optional): A peer ID represented as a CIDv1 encoded with `libp2p-key` codec - - Returned peer records must be closer to `peer-id` than `closerThan` +- `closer-than` (optional): A peer ID represented as a CIDv1 encoded with `libp2p-key` codec + - Returned peer records must be closer to `peer-id` than `closer-than` - If omitted, the routing implementation should use its own peer ID - `count` (optional): Number of peer records to return - Minimum 1, maximum 100, default 20 diff --git a/src/routing/http-routing-v1.md b/src/routing/http-routing-v1.md index 1631b29a6..8815faf7c 100644 --- a/src/routing/http-routing-v1.md +++ b/src/routing/http-routing-v1.md @@ -246,7 +246,7 @@ The content body must be a [`application/vnd.ipfs.ipns-record`][application/vnd. ## DHT Routing API -### `GET /routing/v1/dht/closest/peers/{peer-id}?[closerThan]&[count]` +### `GET /routing/v1/dht/closest/peers/{peer-id}` #### Path Parameters @@ -254,8 +254,8 @@ The content body must be a [`application/vnd.ipfs.ipns-record`][application/vnd. #### Query Parameters -- `closerThan` is an optional [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md) represented as a CIDv1 encoded with `libp2p-key` codec. - - Returned peer records must be closer to `peer-id` than `closerThan`. +- `closer-than` is an optional [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md) represented as a CIDv1 encoded with `libp2p-key` codec. + - Returned peer records must be closer to `peer-id` than `closer-than`. - If omitted the routing implementation should use its own [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md). - `count` is an optional number that specifies how many peer records the requester desires. - Minimum 1, maximum 100, default 20. From 48fe4cfe5c0815e0e74330007e1f8e0681719dea Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 17 Oct 2025 17:18:58 +0200 Subject: [PATCH 09/12] routing/http: simplify GetClosestPeers endpoint changed path parameter from {peer-id} to {key} to accept both CIDs and Peer IDs, matching actual DHT usage where closest peers can be queried for arbitrary keys removed count and closer-than query parameters that were adding complexity without clear use cases in practice clarified response size should match DHT bucket size (20 for Amino DHT) added note that this optional endpoint helps light clients lower the cost of DHT walks in browser contexts --- src/routing/http-routing-v1.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/routing/http-routing-v1.md b/src/routing/http-routing-v1.md index 8815faf7c..d16489d8b 100644 --- a/src/routing/http-routing-v1.md +++ b/src/routing/http-routing-v1.md @@ -246,19 +246,16 @@ The content body must be a [`application/vnd.ipfs.ipns-record`][application/vnd. ## DHT Routing API -### `GET /routing/v1/dht/closest/peers/{peer-id}` +### `GET /routing/v1/dht/closest/peers/{key}` -#### Path Parameters - -- `peer-id` is a [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md) represented as a CIDv1 encoded with `libp2p-key` codec. +This optional endpoint allows light clients to lower the cost of DHT walks in browser contexts. -#### Query Parameters +#### Path Parameters -- `closer-than` is an optional [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md) represented as a CIDv1 encoded with `libp2p-key` codec. - - Returned peer records must be closer to `peer-id` than `closer-than`. - - If omitted the routing implementation should use its own [Peer ID](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md). -- `count` is an optional number that specifies how many peer records the requester desires. - - Minimum 1, maximum 100, default 20. +- `key` is a [CID] or [Peer ID][peer-id-representation] to find the closest peers to. + - [CID] should be a CIDv1 in any encoding. + - [Peer ID][peer-id-representation] can be represented as a Multihash in Base58btc, or a CIDv1 with `libp2p-key` (`0x72`) codec in Base36 or Base32. + - Implementations SHOULD support both CID and Peer ID formats for maximum interoperability. #### Response Status Codes @@ -294,7 +291,7 @@ The content body must be a [`application/vnd.ipfs.ipns-record`][application/vnd. } ``` -The number of peer records in the responses SHOULD be limited to the `count` query parameter, which defaults to 20 if unspecified. +The number of peer records in the response SHOULD be limited to the DHT bucket size (20 for Amino DHT). The client SHOULD be able to make a request with `Accept: application/x-ndjson` and get a [stream](#streaming) with more results. From 463591e00f09033a45dd44dbf11166d05e38919f Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Thu, 20 Nov 2025 20:25:01 +0100 Subject: [PATCH 10/12] docs(routing): align IPIP-476 with someguy v0.11.0 implementation Final ratification cleanup aligning specs with actual shipped implementation. Changes to IPIP-0476: - change path parameter from {peer-id} to {key} - remove unimplemented query parameters (count, closer-than) - document query parameters in Alternatives section with rationale - add peer sorting requirement (XOR distance for Kademlia DHTs) - update test fixtures to reflect actual API surface - note about raw codec for arbitrary multihash lookups - update date to 2025-11-20 Changes to http-routing-v1.md: - add raw codec documentation for arbitrary multihash lookups - clarify peer sorting requirement (SHOULD, XOR distance) - fix streaming description (remove "more results" language) - add note about streaming being blocked until DHT lookup completes - reorganize frontmatter: move inactive editors to former_editors - add contributors to thanks section - update date to 2025-11-20 Both specs now accurately reflect the API shipped in: - boxo v0.35.2 (https://github.com/ipfs/boxo/pull/1021) - someguy v0.11.0 (https://github.com/ipfs/someguy/releases/tag/v0.11.0) --- src/ipips/ipip-0476.md | 40 +++++++++++++++++------------- src/routing/http-routing-v1.md | 45 +++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/ipips/ipip-0476.md b/src/ipips/ipip-0476.md index 6447fd68c..20e1c4771 100644 --- a/src/ipips/ipip-0476.md +++ b/src/ipips/ipip-0476.md @@ -1,6 +1,6 @@ --- title: "IPIP-0476: Delegated Routing DHT Closest Peers API" -date: 2025-08-19 +date: 2025-11-20 ipip: proposal editors: - name: Alex Potsides @@ -42,19 +42,14 @@ This is particularly problematic for: This IPIP introduces a new "DHT Routing API" section to the Delegated Routing V1 HTTP API specification with the following endpoint: -### `GET /routing/v1/dht/closest/peers/{peer-id}` +### `GET /routing/v1/dht/closest/peers/{key}` #### Path Parameters -- `peer-id`: The target peer ID to find closest peers for, represented as a CIDv1 encoded with `libp2p-key` codec - -#### Query Parameters - -- `closer-than` (optional): A peer ID represented as a CIDv1 encoded with `libp2p-key` codec - - Returned peer records must be closer to `peer-id` than `closer-than` - - If omitted, the routing implementation should use its own peer ID -- `count` (optional): Number of peer records to return - - Minimum 1, maximum 100, default 20 +- `key` is a [CID](https://github.com/multiformats/cid) or Peer ID to find the closest peers to. + - CID SHOULD be a CIDv1 in any encoding. + - Peer ID can be represented as a Multihash in Base58btc, or a CIDv1 with `libp2p-key` (`0x72`) codec in Base36 or Base32. + - Arbitrary multihash lookups can be performed by wrapping the multihash in a CIDv1 with `raw` (`0x55`) codec. #### Response @@ -65,7 +60,7 @@ The response follows the same format as the existing peer routing endpoints, ret The following changes are made to `src/routing/http-routing-v1.md`: 1. Add a new "## DHT Routing API" section after the "Peer Routing API" section -2. Document the `/routing/v1/dht/closest/peers/{peer-id}` endpoint with its parameters and response format +2. Document the `/routing/v1/dht/closest/peers/{key}` endpoint with its parameters and response format 3. Update the document date to reflect the modification ## Design rationale @@ -78,7 +73,7 @@ The endpoint provides the minimum viable functionality needed for the primary us ### Future-Proofed Path Structure -The path `/routing/v1/dht/closest/peers/{peer-id}` is intentionally structured to allow future expansion: +The path `/routing/v1/dht/closest/peers/{key}` is intentionally structured to allow future expansion: - The `/dht/` segment clearly indicates DHT-specific operations - The `/closest/peers/` structure allows for potential future endpoints like `/closest/keys/` for keyspace queries - This organization keeps the API logical and extensible @@ -131,14 +126,25 @@ Several alternatives were considered: 4. **Amino-Specific Endpoint**: Initially considered `/routing/v1/amino/` namespace but rejected in favor of the more generic `/dht/` approach +5. **Query Parameters for Filtering and Limiting**: During design discussions, `count` and `closer-than` query parameters were considered: + - `count`: limit the number of results returned + - `closer-than`: filter peers to only return those closer than a reference peer + + These were omitted from the initial implementation (boxo v0.35.2, someguy v0.11.0) for simplicity. The endpoint defaults to returning up to the DHT bucket size (20 for Amino DHT). Future backwards-compatible updates MAY add these parameters if use cases emerge that require more fine-grained control. + +### Peer Ordering + +Implementations SHOULD return peers sorted by closeness to the key. For Kademlia-based DHT implementations (such as Amino DHT), this means sorting by XOR distance with the closest peers first. + ## Test fixtures This IPIP does not deal with content-addressed data, so specific test CIDs are not applicable. However, implementations should test: -1. Valid peer ID inputs return appropriate closest peers -2. The `closerThan` parameter correctly filters results -3. The `count` parameter limits results as specified -4. Invalid peer IDs return appropriate error responses +1. Valid CID and Peer ID inputs return appropriate closest peers +2. Results are limited to the DHT bucket size +3. Peers are sorted by closeness (XOR distance for Kademlia DHTs) +4. Invalid keys return appropriate error responses +5. Both streaming (NDJSON) and non-streaming (JSON) response formats work correctly ### Copyright diff --git a/src/routing/http-routing-v1.md b/src/routing/http-routing-v1.md index b85d9bb6d..257ba4ab7 100644 --- a/src/routing/http-routing-v1.md +++ b/src/routing/http-routing-v1.md @@ -4,9 +4,28 @@ description: > 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 an HTTP API for delegated routing of content, peers, and IPNS. -date: 2025-08-19 +date: 2025-11-20 maturity: reliable editors: + - name: Marcin Rataj + github: lidel + url: https://lidel.org/ + affiliation: + name: Shipyard + url: https://ipshipyard.com +former_editors: + - name: Henrique Dias + url: https://hacdias.com/ + github: hacdias + affiliation: + name: Shipyard + url: https://ipshipyard.com + - name: Daniel Norman + github: 2color + affiliation: + name: Shipyard + url: https://ipshipyard.com +thanks: - name: Gus Eggert github: guseggert affiliation: @@ -17,20 +36,15 @@ editors: affiliation: name: Protocol Labs url: https://protocol.ai/ - - name: Henrique Dias - url: https://hacdias.com/ - github: hacdias + - name: Alex Potsides + github: achingbrain affiliation: name: Shipyard url: https://ipshipyard.com - - name: Marcin Rataj - github: lidel - url: https://lidel.org/ - affiliation: - name: Shipyard - url: https://ipshipyard.com - - name: Daniel Norman - github: 2color + - name: Will Scott + github: willscott + - name: Hector Sanjuan + github: hsanjuan affiliation: name: Shipyard url: https://ipshipyard.com @@ -254,8 +268,9 @@ This optional endpoint allows light clients to lower the cost of DHT walks in br #### Path Parameters - `key` is a [CID] or [Peer ID][peer-id-representation] to find the closest peers to. - - [CID] should be a CIDv1 in any encoding. + - [CID] SHOULD be a CIDv1 in any encoding. - [Peer ID][peer-id-representation] can be represented as a Multihash in Base58btc, or a CIDv1 with `libp2p-key` (`0x72`) codec in Base36 or Base32. + - Arbitrary multihash lookups can be performed by wrapping the multihash in a CIDv1 with `raw` (`0x55`) codec. - Implementations SHOULD support both CID and Peer ID formats for maximum interoperability. #### Response Status Codes @@ -294,7 +309,9 @@ This optional endpoint allows light clients to lower the cost of DHT walks in br The number of peer records in the response SHOULD be limited to the DHT bucket size (20 for Amino DHT). -The client SHOULD be able to make a request with `Accept: application/x-ndjson` and get a [stream](#streaming) with more results. +Peers SHOULD be returned sorted by closeness to the key. For Kademlia-based DHT implementations (such as Amino DHT), this means sorting by XOR distance with the closest peers first. + +The client SHOULD be able to make a request with `Accept: application/x-ndjson` and get a [stream](#streaming) with results. Note that due to the XOR sorting requirement, the streamed response may be blocked until the DHT lookup completes and peers can be sorted before transmission. Each object in the `Peers` list is a record conforming to the [Peer Schema](#peer-schema). From 3bfb19332c83ec42c6f69bdfc2b9984598133cfe Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Thu, 20 Nov 2025 20:46:11 +0100 Subject: [PATCH 11/12] chore(ipip-0476): mark as ratified --- src/ipips/ipip-0476.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ipips/ipip-0476.md b/src/ipips/ipip-0476.md index 20e1c4771..3b38df703 100644 --- a/src/ipips/ipip-0476.md +++ b/src/ipips/ipip-0476.md @@ -1,7 +1,7 @@ --- title: "IPIP-0476: Delegated Routing DHT Closest Peers API" date: 2025-11-20 -ipip: proposal +ipip: ratified editors: - name: Alex Potsides github: achingbrain From d551c94c069d7d406a407809c0d2d8603fdb8a34 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Thu, 20 Nov 2025 20:51:21 +0100 Subject: [PATCH 12/12] docs(routing): clarify DHT API is optional, accept 404 or 501 --- src/routing/http-routing-v1.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/routing/http-routing-v1.md b/src/routing/http-routing-v1.md index 257ba4ab7..8fc1f8006 100644 --- a/src/routing/http-routing-v1.md +++ b/src/routing/http-routing-v1.md @@ -261,6 +261,8 @@ The content body must be a [`application/vnd.ipfs.ipns-record`][application/vnd. ## DHT Routing API +The DHT Routing API is OPTIONAL. Implementations that do not support DHT operations MAY return `404` (Not Found) or `501` (Not Implemented) as specified in [Error Codes](#error-codes). + ### `GET /routing/v1/dht/closest/peers/{key}` This optional endpoint allows light clients to lower the cost of DHT walks in browser contexts. @@ -278,6 +280,7 @@ This optional endpoint allows light clients to lower the cost of DHT walks in br - `200` (OK): the response body contains peer 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. +- `501` (Not Implemented): may be returned if DHT operations are not supported. #### Response Headers