Skip to content
LogoLogo

Remote stores

Where the signed DDO actually lives

In v1 the DDO went on chain. In v2 it does not: nautilus signs the document as a verifiable credential, stores it somewhere off chain, and writes only a pointer — { remote: <StorageObject> } — into the NFT's metadata.

That is why publishing needs a remote store, and why publish throws without one.

Loading diagram...

The publish path: the document is signed and stored off chain, and only its pointer is written on chain, where ocean-node dereferences it to index the asset.

The default: the node's own storage

NodePersistentRemoteStore writes to ocean-node's persistent storage, so you need no external infrastructure at all. It takes a node client, which is why the instance is built in two steps.

const  = await .(, {  })
 
const  = await .(, {
  ,
  : new (.()) 
})

It creates a bucket on demand. Pass bucketId to reuse one, accessLists to restrict who can read it, or label to name it.

IPFS

const  = new ({ 
  : 'http://127.0.0.1:5001/api/v0/add'
}) 

uploadUrl is the Kubo RPC add endpoint. Add headers for an authenticated gateway, and extractCid if your gateway returns the CID under a key other than the usual Hash/cid/IpfsHash.

What ocean-node accepts

The node's isRemoteDDO accepts an object with exactly one key, remote, and hands it to the same storage resolver it uses for asset files. So any storage type it supports for files works for the DDO too:

typepointer shape
ipfs{ type, hash }
url{ type, url, method }
arweave{ type, transactionId }
s3{ type, s3Access }
ftp{ type, url }
nodePersistentStorage{ type, bucketId, fileName }

Writing your own

The interface is one method.

class  implements RemoteStore {
  async (: string, : { : string }): <> { 
    const  = await (, `${.}.json`)
 
    return { : 'url', , : 'GET' } as 
  }
}
 
declare function (: string, : string): <string>

payload is the signed DDO. hint.did is a stable name, for stores that need a key. Return any pointer shape from the table above — nautilus wraps it with toRemotePointer before it goes on chain.

Overriding per call

The instance's store is the default; a single publish can use a different one.

await .(, {
  : new ({ : 'http://127.0.0.1:5001/api/v0/add' }) 
})