Clarification over TTL and LifeTime for IPNS Records

When using the HTTP API for IPNS publishing, you have the option to set a ttl and lifetime value. Yesterday I was poking around the internals of the IPNS system within go-ipfs and noticed that there is only one concept of ā€œtime durationā€ and that is a TTL.

There are functions that can be used to publish records within the namesys system. Here is the interface

// Publisher is an object capable of publishing particular names.
type Publisher interface {

	// Publish establishes a name-value mapping.
	// TODO make this not PrivKey specific.
	Publish(ctx context.Context, name ci.PrivKey, value path.Path) error

	// TODO: to be replaced by a more generic 'PublishWithValidity' type
	// call once the records spec is implemented
	PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error
}

When poking around further, the only difference between Publish and PublishWithEOL is that Publish uses a default EOL value, and feeds that into PublishEOL

func (ns *mpns) PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error {
	id, err := peer.IDFromPrivateKey(name)
	if err != nil {
		return err
	}
	if err := ns.ipnsPublisher.PublishWithEOL(ctx, name, value, eol); err != nil {
		return err
	}
	ttl := DefaultResolverCacheTTL
	if ttEol := eol.Sub(time.Now()); ttEol < ttl {
		ttl = ttEol
	}
	ns.cacheSet(peer.IDB58Encode(id), value, ttl)
	return nil
}

Publish looks like the following

// Publish implements Publisher
func (ns *mpns) Publish(ctx context.Context, name ci.PrivKey, value path.Path) error {
	return ns.PublishWithEOL(ctx, name, value, time.Now().Add(DefaultRecordTTL))
}

where DefaultRecordTTL is const DefaultRecordTTL = 24 * time.Hour

From what I can gather, there is no lifetime value anywhere, and instead the only time expiration value is TTL.

So my question(s):

  1. What is the difference between ttl and lifetime
  2. Why does the namesys system not have 2 time expiration values, like the API which has concepts of ttl and lifetime
  • lifetime indicates for how long a given record is valid (maximum time this record will be valid for)
  • ttl is a optional value saying how long a record can be cached for (minimum time this record will be valid for / acceptable).

TTL option is currently experimental, and itā€™s been wired through in a slightly weird way - through a context value - https://github.com/ipfs/go-ipfs/blob/d40c69f56e3f2abfa8a72d3213a5ca1e8af1ab98/namesys/publisher.go#L204

Thanks Magik6k! So if I want to set the TTL, I should set that within the context that I pass into Publish? That would mean that the time.Time value passed in for eol is the lifetime?

Thanks :smiley:

You really shouldnā€™t need to use namesys Publish directly (or any other internal component really) - And instead use it through coreapi:

api := coreapi.NewCoreAPI(ipfsNode)
api.Name().Publish(ctx, path, optionl.Name.ValidTime(validTime), options.Name.TTL(ttl))

The issue that I run into with that, is it expects the keys being used to be available. For security reasons my project uses a slightly different approach to key management, through an encrypted volume of IPFS keys. Although this keystore adheres to the Keystore interface, without significant integration work into go-ipfs, it is not possible to expose the keys in a manner (as far as I can tell) that the coreapi uses.

The reason why Iā€™m using the namesys Publish directly, is because I can feed in the private key being used, as opposed to coreapi (please correct me if Iā€™m wrong) doesnā€™t have that ability, and instead will perform a lookup of the key based on itā€™s name.

For reference, here is the keystore which uses badger as the underlying datastore. Consequently, this means that I can only have 1 process running as a reader/writer, or many reader processes. For my project, this is not suitable, so in order to get around that issue, the keystore is exposed over grpc which is why Iā€™m using the namesys Publish as it allows me to feed the private key directly into the call, as opposed to feeding in the name, which will then cause a lookup. So ultimately, the easiest way seemed to be to construct a thin client solely used for publishing IPNS records.

Granted, unless Iā€™m missing something and I can feed in the private key directly to coreapi I would gladly use it.

I see.

Basically, we donā€™t want to expose sensitive data (private keys) through the API, at least not until we implement https://github.com/ipfs/go-ipfs/issues/2389.

I think it should be fine to add private key support to coreapi though, because:

  • CoreAPI constructed from go-ipfs node has access to this anyways
  • CoreAPI over HTTP/RPC can just return ā€˜not supportedā€™ until security tokens are implemented

Would this help with your use case?

That makes sense! Up until now iā€™ve just been using an API created with gin-gonic to forward all requests to the IPFS API directly while still being able to implement access control.

Having private key support available through coreapi would be fantastic and absolutely help with my use case :smiley: