Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.11.0] - 2026-02-24
### Added
- New test `TestDNSLinkGatewayIPNS` for DNSLink TXT records pointing at `/ipns/<key>` instead of `/ipfs/<cid>`. All prior DNSLink tests only covered the `/ipfs/` case. [#272](https://github.com/ipfs/gateway-conformance/pull/272)
- New test cases in `TestSubdomainGatewayDNSLinkInlining` for the subdomain form of DNSLink -> IPNS resolution (`{dnslink}.ipns.{gateway}` and `{inlined-dnslink}.ipns.{gateway}`). [#272](https://github.com/ipfs/gateway-conformance/pull/272)

## [0.10.1] - 2026-02-13
### Fixed
- Renamed `Base32Cid()` to `DNSSafeCidV1()` and fixed it to convert CIDv0 to CIDv1 before encoding. Uses base36 for `libp2p-key` codec (IPNS keys) and base32 for everything else, producing valid DNS-safe CID strings for subdomain gateway tests. [#264](https://github.com/ipfs/gateway-conformance/issues/264)
Expand Down
10 changes: 10 additions & 0 deletions fixtures/dnslink_ipns/dnslink.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# yaml-language-server: $schema=../fixture.schema.json
dnslinks:
dnslink-over-ipns:
domain: dnslink-over-ipns.example.org
# Ed25519 IPNS key from subdomain_gateway fixtures (base58btc peer ID)
path: /ipns/12D3KooWLQzUv2FHWGVPXTXSZpdHs7oHbXub2G5WC8Tx4NQhyd2d
dnslink-over-ipns-cidv1:
domain: dnslink-over-ipns-cidv1.example.org
# same Ed25519 key as above, but using CIDv1 with libp2p-key multicodec (base36)
path: /ipns/k51qzi5uqu5dk3v4rmjber23h16xnr23bsggmqqil9z2gduiis5se8dht36dam
56 changes: 56 additions & 0 deletions tests/dnslink_gateway_ipns_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package tests

import (
"testing"

"github.com/ipfs/gateway-conformance/tooling"
"github.com/ipfs/gateway-conformance/tooling/car"
"github.com/ipfs/gateway-conformance/tooling/dnslink"
"github.com/ipfs/gateway-conformance/tooling/specs"
. "github.com/ipfs/gateway-conformance/tooling/test"
)

func TestDNSLinkGatewayIPNS(t *testing.T) {
tooling.LogTestGroup(t, GroupDNSLink)

dnsLinks := dnslink.MustOpenDNSLink("dnslink_ipns/dnslink.yml")
dnsLinkB58 := dnsLinks.MustGet("dnslink-over-ipns")
dnsLinkCIDv1 := dnsLinks.MustGet("dnslink-over-ipns-cidv1")

// same content the Ed25519 IPNS record from subdomain_gateway points at
fixture := car.MustOpenUnixfsCar("subdomain_gateway/fixtures.car")
payload := string(fixture.MustGetRawData("hello-CIDv1"))

tests := SugarTests{
{
Name: "GET for DNSLink with dnslink=/ipns/{peer-id} returns expected payload",
Hint: `
When a DNSLink TXT record points to /ipns/<peer-id> (base58btc),
the gateway must resolve the IPNS name and then serve the content
it points to.
`,
Request: Request().
Header("Host", dnsLinkB58).
Path("/"),
Response: Expect().
Status(200).
Body(payload),
},
{
Name: "GET for DNSLink with dnslink=/ipns/{cidv1-libp2p-key} returns expected payload",
Hint: `
When a DNSLink TXT record points to /ipns/<cidv1-libp2p-key> (base36),
the gateway must resolve the IPNS name and then serve the content
it points to.
`,
Request: Request().
Header("Host", dnsLinkCIDv1).
Path("/"),
Response: Expect().
Status(200).
Body(payload),
},
}

RunWithSpecs(t, tests, specs.DNSLinkGateway)
}
39 changes: 39 additions & 0 deletions tests/subdomain_gateway_ipns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,5 +236,44 @@ func TestSubdomainGatewayDNSLinkInlining(t *testing.T) {
},
}...)

// DNSLink with dnslink=/ipns/{key} (resolves through IPNS)
dnsLinksIPNS := dnslink.MustOpenDNSLink("dnslink_ipns/dnslink.yml")
dnsLinkOverIPNS := dnsLinksIPNS.MustGet("dnslink-over-ipns")
dnsLinkOverIPNSCIDv1 := dnsLinksIPNS.MustGet("dnslink-over-ipns-cidv1")

fixture := car.MustOpenUnixfsCar("subdomain_gateway/fixtures.car")
ipnsPayload := string(fixture.MustGetRawData("hello-CIDv1"))

for _, dnsLinkIPNS := range []struct {
name string
domain string
}{
{"peer-id", dnsLinkOverIPNS},
{"cidv1-libp2p-key", dnsLinkOverIPNSCIDv1},
} {
tests = append(tests, SugarTests{
{
Name: "request for {dnslink}.ipns.{gateway} with dnslink=/ipns/{" + dnsLinkIPNS.name + "} returns expected payload",
Hint: "DNSLink TXT record pointing at /ipns/<key> must be resolved recursively via IPNS",
Request: Request().
Header("Host", Fmt("{{dnslink}}.ipns.{{host}}", dnsLinkIPNS.domain, u.Host)).
Path("/"),
Response: Expect().
Status(200).
Body(ipnsPayload),
},
{
Name: "request for {inlined-dnslink}.ipns.{gateway} with dnslink=/ipns/{" + dnsLinkIPNS.name + "} returns expected payload",
Hint: "DNSLink TXT record pointing at /ipns/<key> must be resolved recursively via IPNS (inlined form)",
Request: Request().
Header("Host", Fmt("{{inlined}}.ipns.{{host}}", dnslink.InlineDNS(dnsLinkIPNS.domain), u.Host)).
Path("/"),
Response: Expect().
Status(200).
Body(ipnsPayload),
},
}...)
}

RunWithSpecs(t, tests, specs.SubdomainGatewayIPNS)
}
Loading