diff --git a/src/http-gateways/path-gateway.md b/src/http-gateways/path-gateway.md index 84538837..22a264db 100644 --- a/src/http-gateways/path-gateway.md +++ b/src/http-gateways/path-gateway.md @@ -4,7 +4,7 @@ description: > The comprehensive low-level HTTP Gateway enables the integration of IPFS resources into the HTTP stack through /ipfs and /ipns namespaces, supporting both deserialized and verifiable response types. -date: 2026-03-05 +date: 2026-08-11 maturity: reliable editors: - name: Marcin Rataj @@ -496,17 +496,68 @@ in caches. Returned directive depends on requested content path and format: - `Cache-Control: public, max-age=29030400, immutable` MUST be returned for - every immutable resource under `/ipfs/` namespace. + every immutable resource under the `/ipfs/` namespace, except generated + responses described below. + - A generated response is one whose body is markup produced by the gateway + rather than the data addressed by the CID: a UnixFS directory listing + rendered as HTML, or an HTML preview of a `dag-cbor`/`dag-json` document. + Deterministic format conversions (such as `dag-cbor` returned as + `dag-json`) are not generated responses and keep the `immutable` + directive. + - Generated `/ipfs/` responses are not byte-for-byte stable: their markup + can change between implementations and versions, so they MUST NOT be + marked `immutable`, and SHOULD be given an expiration that lets caches + pick up regenerated markup. The underlying data is content-addressed and + never expires, so a stale response can only show outdated markup, never + invalid data, and a fixed stale window is safe. The RECOMMENDED window is + `public, max-age=604800, stale-while-revalidate=2678400` (one week, plus + a 31-day stale period). This fixed window applies only under the + immutable `/ipfs/` namespace. The same listing reached through `/ipns/` + is mutable and follows the rules below. - `Cache-Control: public, max-age=` SHOULD be returned for mutable - resources under `/ipns/{id-with-ttl}/` namespace; `max-age=` SHOULD - indicate remaining TTL of the mutable pointer such as :cite[ipns-record] or DNSLink - TXT record. - - Implementations MAY place an upper bound on any TTL received, as - noted in Section 8 of :cite[rfc2181]. - - If TTL value is unknown, implementations SHOULD not send a `Cache-Control` - - No matter if TTL value is known or not, implementations SHOULD always - send a [`Last-Modified`](#last-modified-response-header) header with the timestamp of the record resolution. + resources under the `/ipns/{id-with-ttl}/` namespace, where `` is + derived from the TTL of the mutable pointer, expressed in seconds: the + :cite[ipns-record] `TTL` field (nanoseconds, converted to seconds) or the + DNSLink DNS TXT record TTL (already in seconds). + - When serving from a cached resolution, implementations SHOULD lower + `` by the time elapsed since the resolution, so layered caches do + not extend the effective freshness beyond the pointer's TTL. + - Implementations MAY place an upper bound on any TTL received, as noted in + Section 8 of :cite[rfc2181]. + - A `stale-while-revalidate` or `stale-if-error` window MAY be added to + improve CDN and offline behavior, but it MUST respect the pointer's + expiration. An :cite[ipns-record] with `ValidityType=0` is cryptographically + valid only until its `Validity` (EOL) timestamp, and a validating client + rejects it once that timestamp passes. When the EOL is known, `max-age` + SHOULD NOT exceed the remaining validity, and the stale window SHOULD be + sized so that `max-age` plus the stale window ends at the EOL and never + crosses it; a fixed stale window can push reuse past the EOL. A response + backed by an already-expired record, or by a record whose `ValidityType` + is unrecognized and whose EOL is therefore unknown, SHOULD be served with + `Cache-Control: no-store`. For the raw record response + (`format=ipns-record`), whose body is the signed record itself, the + entire cache lifetime (`max-age` plus any stale window) SHOULD stay + within the remaining validity. A DNSLink pointer carries no signature + EOL and MAY use a bounded best-effort stale window. + - A generated response reached through `/ipns/`, such as a directory listing, + is mutable even though its body is regenerated markup. Apply the `/ipns/` + `max-age` and stale-window rules above, not the fixed window for generated + `/ipfs/` responses: the pointer it renders can move and, for an + :cite[ipns-record], expire, so the response SHOULD NOT be cached past the + record's EOL. + - If the TTL is unknown, implementations MAY send a best-effort + `Cache-Control` telling caches and CDNs how long a stale response is + acceptable. The EOL bound above still applies: when the record's EOL is + known, the freshness lifetime SHOULD NOT extend past it. + - Implementations SHOULD send a + [`Last-Modified`](#last-modified-response-header) header with the + timestamp of the record resolution alongside `Cache-Control`: the + explicit directive takes precedence over heuristic freshness, and the + timestamp enables inexpensive update checks via `If-Modified-Since`. + Sending `Last-Modified` without `Cache-Control` triggers heuristic + freshness; see the + [`Last-Modified`](#last-modified-response-header) section. ### `Last-Modified` (response header) diff --git a/src/routing/http-routing-v1.md b/src/routing/http-routing-v1.md index 6d0a2271..1231c817 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: 2026-07-29 +date: 2026-08-11 maturity: reliable editors: - name: Marcin Rataj @@ -233,9 +233,11 @@ Each object in the `Peers` list is a record conforming to the [Peer Schema](#pee #### 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. +- `Cache-Control: public, max-age={ttl}, stale-while-revalidate={stale}, stale-if-error={stale}`: cache lifetime for the returned :ref[IPNS Record]. + - An IPNS Record with `ValidityType=0` is valid only until its EOL, the `Validity` timestamp. A cache that reuses the record past the EOL serves a record that fails validation, so the entire cache lifetime MUST stay within the remaining validity. + - `{ttl}` (the `max-age`) SHOULD match `IpnsEntry.data[TTL]` converted to seconds, or default to the suggested default TTL from :cite[ipns-record] (300 seconds) when `TTL` is absent. A `TTL` of `0` maps to `max-age=0`, leaving the whole remaining validity as the stale window. Either way, `max-age` MUST NOT exceed the remaining validity. + - `{stale}` SHOULD be the remaining validity minus `max-age`, so `max-age` plus the stale window ends at the EOL and never crosses it. + - Servers SHOULD return `Cache-Control: no-store` instead when the record is already expired, or when its `ValidityType` is unrecognized and the EOL is therefore unknown. - `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.