naam

package module
v0.0.0-...-73d6f8d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 2, 2024 License: Apache-2.0, MIT Imports: 34 Imported by: 2

README

go-naam

🚧 Experimental.

The Golang implementation of IPNI Naam protocol.

Publish and resolve IPNS records using IPNI advertisements and lookup.

Overview

  • IPNS specifies how a fixed key maps to an IPNS record (mutable pointer to object)
    • IPNS Name is a fixed key (hash of public key) that maps to an IPNS record
    • IPNS Record is mutable signed info about some object, such as an IPFS path.
    • Allows changing the object that an IPNS name refers to.
  • Naam is a specification for how to use IPNI to resolve IPNS names to an IPNS records.
    • Specifies how to publish IPNS records to IPNI
    • Specifies how a client queries IPNI, using an IPNS name to lookup an IPNS record.
Publishing IPNS
  • The publisher creates a unique IPNS name to use. This is created from a public key, and the public key can be extracted from the IPNS name.
  • The publisher creates an IPNS record that contains the CID of the object that the IPNS name resolves to. The record is signed using the private key that is associated with the public key used to create the name.
  • The publisher creates an IPNI advertisement that contains the IPNS record data and the IONS name as a multihash lookup key for that record.
  • The publisher announces the new advertisement to IPNI, and IPNI ingests the advertisement.
  • Each unique IPNS name has its own chain of advertisements. The chain can be optionally kept as a historical record of the IPNS record updates.
Resolving IPNS
  • A client queries IPNI using the IPNS name in multihash form and receives an IPNS record in response.
  • The client validates the signature of the IPNS record using the public key from the IPNS name. This prevents anyone, except the creator of the IPNS name, from being to publish IPNS records for that name. The resolved CID is extracted from the IPNS record and returned to the client.
  • The client client then retrieves the data associated with that CID, possible from IPFS or other storage.
Updating IPNS
  • The publisher creates a new IPNS record with different data for the IPNS name to resolve to.
  • The new IPNS record is published the same as before and announced to IPNS.
  • Since the context ID and multhash key (IPNS name) are the same as previously, this results in only an update of the IPNS record, so there is no need to remove any previously published records.
Removing IPNS

An IPNS record can be removed from IPNI by publishing a removal advertisement when there is no further use for the IPNS name. This is not strictly necessary since IPNS records support EOL (end-of-life) and TTL (time-to-live).

Example

package main

import (
	"context"
	"errors"
	"fmt"
	"time"

	"github.com/ipfs/boxo/path"
	"github.com/ipfs/go-cid"
	"github.com/ipni/go-naam"
	"github.com/libp2p/go-libp2p"
	"github.com/libp2p/go-libp2p/core/peer"
)

const (
	announceURL = "http://localhost:3001"  // indexer ingest URL (e.g. "https//dev.cid.contact")
	findURL     = "http://localhost:40080" // dhstore service URL (e.g. "https://assigner.dev.cid.contact")
)

func main() {
	ctx := context.TODO()
	someCid, err := cid.Decode("bafzaajaiaejcbzdibmxyzdjbbehgvizh6g5tikvy47mshdy6gwbruvgwvd24seje")
	if err != nil {
		panic(err)
	}

	h, err := libp2p.New()
	if err != nil {
		panic(err)
	}
	n, err := naam.New(naam.WithHost(h),
		naam.WithHttpIndexerURL(announceURL),
		naam.WithHttpFindURL(findURL),
	)
	if err != nil {
		panic(err)
	}

	// Publish IPNS record to IPNI indexer.
	publishedPath := path.FromCid(someCid)
	err = n.Publish(ctx, publishedPath, naam.WithEOL(time.Now().Add(48*time.Hour)))
	if err != nil {
		panic(err)
	}
	ipnsName := n.Name()
	fmt.Println("IPNS record published with name", ipnsName)

	// Resolve locally - avoids indexer lookup if naam instance is the publisher.
	resolvedPath, err := n.Resolve(ctx, ipnsName)
	if err != nil {
		panic(err)
	}
	fmt.Println("Resolved IPNS record locally:", ipnsName, "==>", resolvedPath)

retry:
	time.Sleep(time.Second)

	// Resolve by looking up IPNS record using indexer with reader-privacy.
	resolvedPath, err = naam.Resolve(ctx, ipnsName, findURL)
	if err != nil {
		if errors.Is(err, naam.ErrNotFound) {
			fmt.Println("Name not found on indexer yet, retrying")
			goto retry
		}
		panic(err)
	}
	fmt.Println("🔒 Reader privacy enabled | Resolved IPNS record using indexer:", ipnsName, "==>", resolvedPath)

	// Resolve by looking up IPNS record using indexer without reader-privacy.
	resolvedPath, err = naam.ResolveNotPrivate(ctx, ipnsName, findURL)
	if err != nil {
		panic(err)
	}
	fmt.Println("⚠️  Reader privacy disabled | Resolved IPNS record using indexer:", ipnsName, "==>", resolvedPath)

	// Resolve a name that does not have an IPNS record.
	pid, err := peer.Decode("12D3KooWPbQ26UtFJ48ybpCyUoFYFBqH64DbHGMAKtXobKtRdzFF")
	if err != nil {
		panic(err)
	}
	anotherName := naam.Name(pid)
	resolvedPath, err = naam.Resolve(ctx, anotherName, findURL)
	if !errors.Is(err, naam.ErrNotFound) {
		panic(err)
	}
	fmt.Println("Record for unknown name", anotherName, "not found, as expected")
}

License

SPDX-License-Identifier: Apache-2.0 OR MIT

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	MetadataProtocolID = multicodec.IpnsRecord
	ContextID          = []byte("/ipni/naam")
)
View Source
var ErrNotFound = errors.New("naam: not found")

ErrNotFound is returned when naam fails to find the requested IPNS record.

Functions

func Name

func Name(peerID peer.ID) string

Name returns the key to lookup the IPNS record for the given peer ID.

func Resolve

func Resolve(ctx context.Context, name string, findURL string) (path.Path, error)

Resolve performs a reader-privacy-enabled indexer lookup of the name and returns the IPFS path identified by the name. The multihash lookup request is sent to the host specified in findURL.

func ResolveNotPrivate

func ResolveNotPrivate(ctx context.Context, name string, findURL string) (path.Path, error)

ResolveNotPrivate performs an indexer lookup of the name, without reader privacy, and returns the IPFS path identified by the name. The multihash lookup request is sent to the host specified by findURL.

Types

type Naam

type Naam struct {
	// contains filtered or unexported fields
}

func New

func New(o ...Option) (*Naam, error)

New creates a new Naam instance for publishing IPNS records in an indexer.

func (*Naam) Name

func (n *Naam) Name() string

Name returns the key to lookup the IPNS record published by this Naam instance.

func (*Naam) Publish

func (n *Naam) Publish(ctx context.Context, value path.Path, o ...PublishOption) error

Publish creates a new IPNI advertisement containing an IPNS record for the given path value and a multihash key for the peer. The advertisement is published to the configured indexer.

This allows the peer multihash to lookup an IPNS record using an IPNI indexer. The IPNS record is then resolved to the path that was published in the IPNI advertisement.

func (*Naam) Resolve

func (n *Naam) Resolve(ctx context.Context, name string) (path.Path, error)

Resolve returns the IPFS path identified by the specified name.

If name matches that of this Naam instance, it is fetched from the local datastore. Otherwise, Resolve performs an indexer lookup of the name. Reader-privacy is enabled by default unless the option WithReaderPrivacy(false) was specified.

If doing an indexer lookup, the multihash lookup request is sent to the host specified in the WithHttpFindURL option, or if not specified, then in WithHttpIndexerURL.

type Option

type Option func(*options)

func WithAnnounceURL

func WithAnnounceURL(a string) Option

WithAnnounceURL is the IPNI endpoint to send direct HTTP advertisement announcement messages to. If no values are specified, then the value of defaultAnnounceURL is used.

func WithDatastore

func WithDatastore(ds datastore.Datastore) Option

WithDatastore supplies an existing datastore. An in-memory datastore is created if one is not supplied.

func WithFindURL

func WithFindURL(a string) Option

WithFindURL configures the URL use for lookups. This is the URL of the dhstore that is used for multihash lookup. If not specified the URL configured by WithAnnounceURL is used.

func WithHost

func WithHost(h host.Host) Option

WithHost supplies a libp2p Host. If not specified a new Host is created.

func WithHttpClient

func WithHttpClient(c *http.Client) Option

WithHttpClient supplies an optional HTTP client. If not specified, the default client is used.

func WithLinkSystem

func WithLinkSystem(ls *ipld.LinkSystem) Option

func WithListenAddr

func WithListenAddr(a string) Option

WithListenAddr specifies the address:port that the publisher listens on when serving advertisements over HTTP. If not specified, the default "0.0.0.0:8080" is used.

func WithProviderAddrs

func WithProviderAddrs(addrs ...string) Option

WithProviderAddrs are multiaddrs that get put into advertisements as the provider addresses. If not specified then the publisher addresses are used.

When publishing to a public indexer, this should always be set to a public address so that the advertisement is not rejected.

These addresses to not have a role in resolving IPNS, but may be used to tell an IPNS clients where to get the content corresponding to the CIR in the IPNS record.

func WithPublisherAddrs

func WithPublisherAddrs(addrs ...string) Option

WithPublisherAddrs is the multiaddr that tells IPNI where to fetch advertisements from.

This address must be routable from the indexer. If not specified then the publisher listen addresses are used.

func WithReaderPrivacy

func WithReaderPrivacy(enabled bool) Option

WithReaderPrivacy enables (true) or disables (false) reader-privacy. This is enabled by default.

type PublishOption

type PublishOption func(*publishOptions)

func WithEOL

func WithEOL(eol time.Time) PublishOption

func WithEmbedPublicKey

func WithEmbedPublicKey(epk bool) PublishOption

func WithTTL

func WithTTL(ttl time.Duration) PublishOption

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL