Skip to content
Draft
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ require (
github.com/yosida95/uritemplate/v3 v3.0.2
go.lumeweb.com/configmanager v0.3.30
go.lumeweb.com/ipfs-content v0.1.18
go.lumeweb.com/ipfs-sdk v0.1.95
go.lumeweb.com/ipfs-sdk v0.1.97
go.lumeweb.com/ipfs-sdk/dnsname v0.1.64
go.lumeweb.com/oauth v0.1.6
go.lumeweb.com/portal-sdk v0.1.72
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,10 @@ go.lumeweb.com/ipfs-content v0.1.18 h1:Lq3/A+2kZcS4RK+bpdk+m4iJ95ZgpEa4DKhiH6bQW
go.lumeweb.com/ipfs-content v0.1.18/go.mod h1:idWCsfndMDCsE5LtU0ZBNaFTBAaiuIe50dce0SDJLxw=
go.lumeweb.com/ipfs-sdk v0.1.95 h1:4y3Oj6aTKLTWSzpdjizrQ7wJc/iXhza9MVuEhxbblyo=
go.lumeweb.com/ipfs-sdk v0.1.95/go.mod h1:+V8YkslX65rPa83BbNYLGnhK24i75wqFtXKyltJ6jV4=
go.lumeweb.com/ipfs-sdk v0.1.96 h1:ciXYrRYtTe2cKs+8cXTQ63GRCQVoF4AqWBWriymybkQ=
go.lumeweb.com/ipfs-sdk v0.1.96/go.mod h1:oIMMi/yGovDqd8fAsfBYkoLV+GXcN0hNVmQXQv0ngDA=
go.lumeweb.com/ipfs-sdk v0.1.97 h1:JV35ex2g5zcoN4uwKGaPIVtiiupli0zVfYnh7eR9OYw=
go.lumeweb.com/ipfs-sdk v0.1.97/go.mod h1:VE85lcKSVHWqRHbKPfyWoMKv7aKwdhXBrRODGOIfoxQ=
go.lumeweb.com/ipfs-sdk/dnsname v0.1.64 h1:RTBD+zoXHOYYKreNRoQhyLLKKWp/VgYfr7qojCzKjww=
go.lumeweb.com/ipfs-sdk/dnsname v0.1.64/go.mod h1:1++6EWMiG/BC5UQjlIobId2YcWDVHjHEhdsEzeISKpQ=
go.lumeweb.com/oauth v0.1.6 h1:6c6LrXxMwx5klbq3OshzXjkGwvYVjAQhjX2FxCAgqqg=
Expand Down
36 changes: 32 additions & 4 deletions internal/catalogops/websites_domains.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,11 +213,21 @@ func websitesDomainsRemove(d WebsitesDeps) catalog.Operation {
})
}

const (
// OpWebsitesDomainsVerify is the canonical name of the verify operation.
// Frontend wiring compares against it to switch rendering (e.g. the verify
// outcome renderer), so it lives here next to the definition.
OpWebsitesDomainsVerify = "websites_domains_verify"
// OpWebsitesDomainsDNSRequirements is the canonical name of the
// dns-requirements operation (same need).
OpWebsitesDomainsDNSRequirements = "websites_domains_dns_requirements"
)

// websitesDomainsVerify is the `websites domains verify` operation. Returns
// *ipfs.DomainResponse.
func websitesDomainsVerify(d WebsitesDeps) catalog.Operation {
return catalog.NewOperation(catalog.OperationSpec{
Name: "websites_domains_verify",
Name: OpWebsitesDomainsVerify,
Title: "Verify a domain binding",
Summary: "Verify a domain's DNS delegation",
Description: "Verify that a bound domain's DNS delegation is correctly configured. The domain argument can be the domain name or its numeric binding ID; the owning website is resolved automatically. Returns the domain's status and delegation after verification.",
Expand Down Expand Up @@ -255,7 +265,7 @@ func websitesDomainsVerify(d WebsitesDeps) catalog.Operation {
// operation. Returns *ipfs.DomainResponse.
func websitesDomainsDNSRequirements(d WebsitesDeps) catalog.Operation {
return catalog.NewOperation(catalog.OperationSpec{
Name: "websites_domains_dns_requirements",
Name: OpWebsitesDomainsDNSRequirements,
Title: "DNS requirements for a domain",
Summary: "Show DNS records needed to complete domain delegation",
Description: "Show the DNS records a user must publish to complete delegation for a bound domain. For HNS namespaces this is the delegation bundle (parent NS/GLUE/DS and authoritative NS/TLSA). The domain argument can be the domain name or its numeric binding ID; the owning website is resolved automatically.",
Expand Down Expand Up @@ -283,12 +293,30 @@ func websitesDomainsDNSRequirements(d WebsitesDeps) catalog.Operation {
if err != nil {
return nil, err
}
// *ipfs.DomainResponse
return svc.GetDomainDNSRequirements(ctx, websiteID, domainID)
domain, err := svc.GetDomainDNSRequirements(ctx, websiteID, domainID)
if err != nil {
return nil, err
}
// On-chain bindings no longer carry a delegation bundle: the
// owning website is attached so frontends can derive the
// authoritative records (the _dnslink TXT) from its target.
// Best effort — a fetch failure only loses the derived rows.
website, _ := svc.Get(ctx, websiteID)
return &DomainDNSRequirements{Domain: domain, Website: website}, nil
}),
})
}

// DomainDNSRequirements pairs the dns-requirements response with the owning
// website record. On-chain bindings no longer carry a delegation bundle, so
// frontends derive the authoritative record set (the _dnslink TXT) from the
// website's target. CLI --json prints only the domain response, keeping the
// historical shape; the website is presentation data.
type DomainDNSRequirements struct {
Domain *ipfs.DomainResponse
Website *ipfs.WebsiteItem
}

// websitesDomainsDANERepublish is the `websites domains dane republish`
// operation. Returns *ipfs.DomainDANERepublishResponse.
func websitesDomainsDANERepublish(d WebsitesDeps) catalog.Operation {
Expand Down
44 changes: 42 additions & 2 deletions internal/catalogops/websites_domains_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type domainsService struct {

authErr error
listFn func(ctx context.Context) ([]ipfs.WebsiteItem, error)
getFn func(ctx context.Context, websiteID string) (*ipfs.WebsiteItem, error)
listDomainsFn func(ctx context.Context, websiteID string) ([]ipfs.DomainResponse, error)
bindDomainFn func(ctx context.Context, websiteID string, req ipfs.DomainRequest) (*ipfs.DomainResponse, error)
unbindDomainFn func(ctx context.Context, websiteID string, domainID string) error
Expand Down Expand Up @@ -67,6 +68,13 @@ func (f *domainsService) VerifyDomain(ctx context.Context, websiteID string, dom
return nil, nil
}

func (f *domainsService) Get(ctx context.Context, websiteID string) (*ipfs.WebsiteItem, error) {
if f.getFn != nil {
return f.getFn(ctx, websiteID)
}
return nil, nil
}

func (f *domainsService) GetDomainDNSRequirements(ctx context.Context, websiteID string, domainID string) (*ipfs.DomainResponse, error) {
if f.dnsRequirementsFn != nil {
return f.dnsRequirementsFn(ctx, websiteID, domainID)
Expand Down Expand Up @@ -141,6 +149,38 @@ func TestWebsitesDomainsListResolvesWebsite(t *testing.T) {
}
}

func TestWebsitesDomainsDNSRequirementsAttachesWebsite(t *testing.T) {
// On-chain bindings no longer carry a delegation bundle; the handler must
// attach the owning website so frontends can derive the authoritative
// records (the _dnslink TXT) from its target.
fake := singleWebsiteFixture()
fake.dnsRequirementsFn = func(_ context.Context, websiteID string, domainID string) (*ipfs.DomainResponse, error) {
return &ipfs.DomainResponse{
Id: 3, Domain: "example.test", Namespace: ipfs.DomainNamespaceHNS,
Status: new(ipfs.DomainResponseStatusOnchainManaged),
}, nil
}
fake.getFn = func(_ context.Context, websiteID string) (*ipfs.WebsiteItem, error) {
return &ipfs.WebsiteItem{Id: 7, Domain: "example.test", TargetType: "ipfs", TargetHash: "QmFoo"}, nil
}

op := websitesDomainsDNSRequirements(domainsDeps(t, fake))
res, err := op.Handler().Execute(context.Background(), map[string]any{"domain": "example.test"})
if err != nil {
t.Fatalf("dns-requirements handler: %v", err)
}
result, ok := res.(*DomainDNSRequirements)
if !ok {
t.Fatalf("result type = %T, want *DomainDNSRequirements", res)
}
if result.Domain == nil || result.Domain.Id != 3 {
t.Fatalf("unexpected domain response: %+v", result.Domain)
}
if result.Website == nil || result.Website.TargetHash != "QmFoo" {
t.Fatalf("website not attached: %+v", result.Website)
}
}

func TestWebsitesDomainsListRequiresWebsite(t *testing.T) {
fake := singleWebsiteFixture()
op := websitesDomainsList(domainsDeps(t, fake))
Expand Down Expand Up @@ -221,8 +261,8 @@ func TestWebsitesDomainsDANERepublishTypedResult(t *testing.T) {
if websiteID != "7" || domainID != "3" {
t.Fatalf("RepublishDANE(%q, %q), want (\"7\", \"3\")", websiteID, domainID)
}
tlsa := "_443._tcp.example.test. 60 IN TLSA 3 1 1 abc123"
return &ipfs.DomainDANERepublishResponse{Id: 3, Domain: "example.test", TlsaRecord: &tlsa}, nil
rdata := "3 1 1 abc123"
return &ipfs.DomainDANERepublishResponse{Id: 3, Domain: "example.test", PublishedToManagedZone: true, TlsaRdata: &rdata}, nil
}

op := websitesDomainsDANERepublish(domainsDeps(t, fake))
Expand Down
56 changes: 39 additions & 17 deletions internal/cli/catalog_websites_wiring.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ func websitesActionAdapter(op catalog.Operation) cli.ActionFunc {
// human (non-JSON) output — in --json mode the error document stays machine
// clean, and it no-ops for every non-verify operation.
func renderVerifyGuidance(output Output, op catalog.Operation, err error) {
if op.Name() == "websites_domains_verify" && !output.IsJSON() {
if op.Name() == catalogops.OpWebsitesDomainsVerify && !output.IsJSON() {
renderDNSSelfServiceGuidance(output, err)
}
}
Expand Down Expand Up @@ -380,7 +380,13 @@ func renderWebsitesResult(_ context.Context, c *cli.Command, op catalog.Operatio
// Slices/maps are excluded — a nil slice legitimately means an empty
// result set (e.g. `websites domains list` with no domains) and is handled
// by the renderer's empty-state branches.
// A verify returning (nil, nil) is meaningful: DNS is not resolvable yet,
// so render the not-verified outcome rather than bailing out.
if result != nil && isNilPointerResult(result) {
if op.Name() == catalogops.OpWebsitesDomainsVerify {
renderDomainVerifyResult(output, nil)
return nil
}
Comment on lines 385 to +389

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Bug high

The typed-nil verify branch calls renderDomainVerifyResult(output, nil) without an output.IsJSON() guard, writing human-readable '⏳ not verified yet' text even in --json mode. Under --json, jsonFormatter.Printfln emits the text as a single non-JSON line, so pinner websites domains verify <d> --json on an unresolvable (nil,nil) result produces broken output instead of a machine JSON document, while all sibling branches honor IsJSON. Guard with if output.IsJSON() { return output.PrintJSON(map[string]any{"verified": false, "message": "not verified yet"}) } before calling renderDomainVerifyResult.

if op.Name() == catalogops.OpWebsitesDomainsVerify {
			if output.IsJSON() {
				return output.PrintJSON(map[string]any{"verified": false, "message": "not verified yet"})
			}
			renderDomainVerifyResult(output, nil)
			return nil
		}
Prompt for LLM

File internal/cli/catalog_websites_wiring.go:

Line 385 to 389:

The typed-nil verify branch calls renderDomainVerifyResult(output, nil) without an output.IsJSON() guard, writing human-readable '⏳ not verified yet' text even in --json mode. Under --json, jsonFormatter.Printfln emits the text as a single non-JSON line, so `pinner websites domains verify <d> --json` on an unresolvable (nil,nil) result produces broken output instead of a machine JSON document, while all sibling branches honor IsJSON. Guard with `if output.IsJSON() { return output.PrintJSON(map[string]any{"verified": false, "message": "not verified yet"}) }` before calling renderDomainVerifyResult.

Suggested Code:

if op.Name() == catalogops.OpWebsitesDomainsVerify {
			if output.IsJSON() {
				return output.PrintJSON(map[string]any{"verified": false, "message": "not verified yet"})
			}
			renderDomainVerifyResult(output, nil)
			return nil
		}

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

return fmt.Errorf("%s returned no result", op.Name())
}

Expand Down Expand Up @@ -421,6 +427,7 @@ func renderWebsitesResult(_ context.Context, c *cli.Command, op catalog.Operatio
{"Message", r.Message},
},
})
renderValidationChecks(output, r.Checks)
return nil

case *ipfs.WebsiteConfigResponse:
Expand Down Expand Up @@ -450,6 +457,20 @@ func renderWebsitesResult(_ context.Context, c *cli.Command, op catalog.Operatio
output.Printfln("Website deleted successfully")
return nil

case *catalogops.DomainDNSRequirements:
// websites domains dns-requirements: the domain response plus the
// owning website, from which the renderer derives the authoritative
// records the backend no longer returns for on-chain bindings.
// --json keeps the historical domain-response shape.
if r.Domain == nil {
return fmt.Errorf("no result returned for %s", op.Name())
}
if output.IsJSON() {
return output.PrintJSON(r.Domain)
}
renderDomainDelegation(output, r.Domain, r.Domain.DnsHostingEnabled, r.Website)
return nil

case []ipfs.DomainResponse:
// websites domains list: a website's domain bindings.
if output.IsJSON() {
Expand Down Expand Up @@ -491,8 +512,8 @@ func renderWebsitesResult(_ context.Context, c *cli.Command, op catalog.Operatio
if output.IsJSON() {
return output.PrintJSON(r)
}
if op.Name() == "websites_domains_dns_requirements" {
renderDomainDelegation(output, r, r.DnsHostingEnabled)
if op.Name() == catalogops.OpWebsitesDomainsVerify {
renderDomainVerifyResult(output, r)
return nil
}
renderDomainResponse(output, r)
Expand Down Expand Up @@ -609,20 +630,21 @@ func renderDomainDANEResponse(output Output, r *ipfs.DomainDANERepublishResponse
if r.OwnerName != nil {
ownerName = *r.OwnerName
}
tlsaRecord := ""
if r.TlsaRecord != nil {
tlsaRecord = *r.TlsaRecord
}
output.PrintFields(FieldGroup{
Fields: []Field{
{"ID", fmt.Sprintf("%d", r.Id)},
{"Domain", r.Domain},
{"Namespace", string(r.Namespace)},
{"Status", status},
{"Owner Name", ownerName},
{"TLSA Record", tlsaRecord},
},
})
fields := []Field{
{"ID", fmt.Sprintf("%d", r.Id)},
{"Domain", r.Domain},
{"Namespace", string(r.Namespace)},
{"Status", status},
{"Owner Name", ownerName},
// published_to_managed_zone is a required field and always echoed; a
// false value means the republished TLSA is NOT live in the managed
// zone yet, so the user must not treat the command as a success.
{"TLSA Published", fmt.Sprintf("%t", r.PublishedToManagedZone)},
}
if r.TlsaRdata != nil && *r.TlsaRdata != "" {
fields = append(fields, Field{"TLSA Record", *r.TlsaRdata})
}
output.PrintFields(FieldGroup{Fields: fields})
Comment on lines +639 to +647

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Bug high

The renderDomainDANEResponse function silently reports success when published_to_managed_zone is false, contradicting the code's own comment (lines 628-631) that a false value means the TLSA is NOT live. Return an error from the renderer/caller path or propagate failure through the exit code so a failed zone publication exits non-zero.

	// published_to_managed_zone=false means the TLSA did NOT land in the
	// managed zone; surface it as a failure so the command does not exit 0.
	fields := []Field{
		{"ID", fmt.Sprintf("%d", r.Id)},
		{"Domain", r.Domain},
		{"Namespace", string(r.Namespace)},
		{"Status", status},
		{"Owner Name", ownerName},
		{"TLSA Published", fmt.Sprintf("%t", r.PublishedToManagedZone)},
	}
	if r.TlsaRdata != nil && *r.TlsaRdata != "" {
		fields = append(fields, Field{"TLSA Record", *r.TlsaRdata})
	}
	output.PrintFields(FieldGroup{Fields: fields})
	if !r.PublishedToManagedZone {
		return fmt.Errorf("TLSA was not published to the managed zone")
	}
	return nil
}
Prompt for LLM

File internal/cli/catalog_websites_wiring.go:

Line 628 to 636:

The renderDomainDANEResponse function silently reports success when published_to_managed_zone is false, contradicting the code's own comment (lines 628-631) that a false value means the TLSA is NOT live. Return an error from the renderer/caller path or propagate failure through the exit code so a failed zone publication exits non-zero.

Suggested Code:

	// published_to_managed_zone=false means the TLSA did NOT land in the
	// managed zone; surface it as a failure so the command does not exit 0.
	fields := []Field{
		{"ID", fmt.Sprintf("%d", r.Id)},
		{"Domain", r.Domain},
		{"Namespace", string(r.Namespace)},
		{"Status", status},
		{"Owner Name", ownerName},
		{"TLSA Published", fmt.Sprintf("%t", r.PublishedToManagedZone)},
	}
	if r.TlsaRdata != nil && *r.TlsaRdata != "" {
		fields = append(fields, Field{"TLSA Record", *r.TlsaRdata})
	}
	output.PrintFields(FieldGroup{Fields: fields})
	if !r.PublishedToManagedZone {
		return fmt.Errorf("TLSA was not published to the managed zone")
	}
	return nil
}

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

}

// renderWebsiteItemHuman renders the fields of a single website (used by get,
Expand Down
19 changes: 18 additions & 1 deletion internal/cli/catalog_websites_wiring_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func TestWebsitesCRUDPositionalMapping(t *testing.T) {
}

func TestRenderWebsitesResultRejectsTypedNil(t *testing.T) {
op := catalog.NewOperation(catalog.OperationSpec{Name: "websites_domains_verify"})
op := catalog.NewOperation(catalog.OperationSpec{Name: "websites_get"})

// A handler that returns (nil, nil) surfaces as a typed nil *ipfs.DomainResponse.
var typedNil *ipfs.DomainResponse
Expand All @@ -131,6 +131,23 @@ func TestRenderWebsitesResultRejectsTypedNil(t *testing.T) {
}
}

func TestRenderWebsitesResultVerifyTypedNilRendersNotVerified(t *testing.T) {
// For verify, a typed nil (the handler returned (nil, nil)) means the
// domain's DNS could not be resolved yet — the not-verified outcome, not
// an error.
op := catalog.NewOperation(catalog.OperationSpec{Name: catalogops.OpWebsitesDomainsVerify})
var typedNil *ipfs.DomainResponse
var buf bytes.Buffer
cmd := &cli.Command{}
cmd.Writer = &buf
if err := renderWebsitesResult(context.Background(), cmd, op, typedNil); err != nil {
t.Fatalf("verify typed-nil should render the not-verified outcome, got err: %v", err)
}
if !strings.Contains(buf.String(), "⏳ not verified yet") {
t.Errorf("expected the not-verified outcome on stdout, got: %q", buf.String())
}
}

func TestIsNilPointerResultTypedNil(t *testing.T) {
var p *int
var s []string
Expand Down
6 changes: 6 additions & 0 deletions internal/cli/dns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ func TestKeepWholeValue(t *testing.T) {
{"full table A content wraps", []string{"r1", "www", "A", "1.2.3.4", "300", ""}, 3, false},
{"full table name col wraps", []string{"r1", "www", "A", "1.2.3.4", "300", ""}, 1, false},
{"full table ttl wraps", []string{"r1", "www", "A", "1.2.3.4", "300", ""}, 4, false},
// On-chain TLSA layout [NAME, TYPE, VALUE]
{"onchain tlsa value kept whole", []string{"_443._tcp.example.com", "TLSA", "3 1 1 0a9e..."}, 2, true},
{"onchain dnslink value kept whole", []string{"_dnslink.example.com", "TXT", "dnslink=/ipfs/bafy..."}, 2, true},
{"onchain non-record value wraps", []string{"example.com", "A", "1.2.3.4"}, 2, false},
{"onchain name col wraps", []string{"_443._tcp.example.com", "TLSA", "3 1 1 0a9e..."}, 0, false},
{"onchain type col wraps", []string{"_443._tcp.example.com", "TLSA", "3 1 1 0a9e..."}, 1, false},
}

for _, tt := range tests {
Expand Down
10 changes: 10 additions & 0 deletions internal/cli/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,16 @@ func keepWholeValue(row []string, j int) bool {
if len(row) == 2 {
return (row[0] == "DS" || row[0] == "TLSA") && j == 1
}
// On-chain TLSA table: [NAME, TYPE, VALUE] — the digest is the same
// copyable opaque value as above, just with the owner name (the
// _443._tcp.<domain> record location) promoted to its own column.
if len(row) == 3 && j == 2 {
switch row[1] {
case "TLSA", "DS":
return true
}
return strings.Contains(row[0], "_443._tcp") || strings.Contains(row[0], "_dnslink")
}
// Full DNS record table: [ID, NAME, TYPE, CONTENT, TTL, STATUS]
if len(row) >= 6 && j == 3 {
switch row[2] {
Expand Down
57 changes: 54 additions & 3 deletions internal/cli/websites_domains.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,59 @@ import (
// websites_domains_wizard.go). This file retains the domain rendering helper
// shared by the domains feature.

// renderDomainVerifyResult turns a `websites domains verify` response into an
// outcome the user can act on, instead of the generic binding field table.
// valid mirrors the wizard's notion of a fully validated binding (active, or
// on-chain managed via the namespace TXT token). A nil response means the
// backend could not resolve the domain's DNS yet — still not verified.
func renderDomainVerifyResult(output Output, r *ipfs.DomainResponse) {
if r == nil {
output.Printfln("⏳ not verified yet")
output.Printfln(" The domain's DNS could not be resolved, so validation didn't run.")
output.Printfln(" DNS can take a while to propagate. Re-check with:")
output.Printfln(" pinner websites domains verify <domain>")
return
}

status := ""
if r.Status != nil {
status = string(*r.Status)
}

if domainStatusIsValid(statusOf(r)) {
output.Printfln("✅ %s verified", r.Domain)
output.Printfln(" Status: %s", status)
output.Printfln(" Your site will be served at https://%s", r.Domain)
if statusOnchainManaged(r) {
// Verification only proves ownership through the on-chain TXT
// token — it does not confirm the TLSA was published, and the
// site won't load over HTTPS without it.
output.Printfln(" Make sure the TLSA record is published so the site loads")
output.Printfln(" over HTTPS:")
output.Printfln(" pinner websites domains dns-requirements %s", r.Domain)
}
renderValidationChecks(output, r.Checks)
return
}

output.Printfln("⏳ %s is not verified yet", r.Domain)
output.Printfln(" Status: %s", status)
output.Printfln(" DNS can take a while to propagate. Re-check with:")
output.Printfln(" pinner websites domains verify %s", r.Domain)
output.Printfln(" Check the records the domain needs:")
output.Printfln(" pinner websites domains dns-requirements %s", r.Domain)
renderValidationChecks(output, r.Checks)
}

// renderDomainDelegation prints the DNS delegation bundle the server computes
// for a domain. Rendering is driver-based: the namespace selects a
// context-specific driver (HNS, ICANN, ...) with a neutral generic fallback,
// matching the server's per-namespace DomainProvider design. managed indicates
// whether Pinner manages the domain's DNS, so drivers can omit authoritative
// records the user does not need to configure.
func renderDomainDelegation(output Output, result *ipfs.DomainResponse, managed bool) {
// records the user does not need to configure. website is the owning website
// when the caller has it; on-chain drivers use its target to derive the
// authoritative records the backend no longer returns.
func renderDomainDelegation(output Output, result *ipfs.DomainResponse, managed bool, website *ipfs.WebsiteItem) {
output.Printfln("DNS requirements for %s", result.Domain)

status := ""
Expand All @@ -43,5 +89,10 @@ func renderDomainDelegation(output Output, result *ipfs.DomainResponse, managed
// meaningful per-namespace (e.g. an HNS on-chain managed binding
// serves its DNS from an external contract and has no records to publish),
// so the driver owns the explanation instead of a generic miss here.
defaultDelegationDriver.Render(output, result, managed)
defaultDelegationDriver.Render(output, result, managed, website)

// The backend computes per-record checks (validation token, dnslink,
// TLSA...) on every requirements response; they are the record-level truth
// the user needs to act on, so they render regardless of namespace.
renderValidationChecks(output, result.Checks)
}
Loading
Loading