Skip to content
LogoLogo

Signing

The DDO as a verifiable credential

Because the DDO lives off chain, something has to make it tamper-evident. nautilus signs it as a JWT Verifiable Credential before storing it, and the asset's issuer is whoever signed.

You do not have to configure this. The default works with nothing but the signer you already have.

The default: sign with the Ethereum key

Eip191VcSigner signs with the same key that pays for the transaction, using an alg: 'ETH-EIP191' header. Publishing therefore works with no SSI wallet anywhere in the picture, and the issuer is the signer's address.

// Nothing to configure — this is what you get.
const  = await .(, {
  : { : 'https://ocean-node.dev.pontus-x.eu' }
})

Issuing from a real DID

WaltIdVcSigner signs with a wallet-held key, so the issuer is a proper DID and the credential validates like any other VC.

const  = new ({ : 'https://wallet.example.org' })
 
// Web3 login returns a bearer token; the wallet, key and DID are listed with it.
const {  } = await .()
const [{ :  }] = await .()
const [] = await .(, )
const [{  }] = await .(, )
 
const  = await .(, {
  ,
  : new ({ 
    , 
    , 
    : .., 
    , 
     
  }) 
})

Pair this with setIssuer when you want the document's declared issuer to match.

What you get back

publish returns the credential alongside the rest of the result, and stored says what went to the remote store.

const { ,  } = await .()
 
?. // the compact JWT that was signed
?. // the DID or address that signed it
. // where the encrypted envelope is, e.g. { type: 'ipfs', hash }
. // the on-chain hash: sha256 of that envelope

The JWT is not stored as is. nautilus has the node encrypt it into an envelope, and that envelope is what the store holds and what the on-chain hash covers — see what is stored. ocean-node stores the signature with the indexed DDO but does not verify it.

Inspecting a credential

decodeCredential reads the payload without verifying the signature — useful for debugging what was actually signed.

const  = () 
 
. // the DID
. // the issuer

Writing your own signer

class  implements DdoSigner {
  async (): <string> { 
    return 'did:web:example.org'
  }
 
  async (: <string, unknown>): <SignedCredential> { 
    return { : await (), : await this.() }
  }
}
 
declare function (: <string, unknown>): <string>

getIssuer() is asked first, and its answer is written onto the DDO before the document is validated, signed and returned — so PublishResponse.ddo.issuer always matches the signed claims. It must name the same identity sign() issues as.

sign() must return a compact JWS whose payload is the DDO it was given: that payload is the document the node indexes. nautilus decodes it, unwraps a vc claim as the node does, and compares it with the validated DDO (key order aside) before anything is stored. On top of the DDO, the payload may carry:

  • the JWT registered claims iss, sub, aud, exp, nbf, iat and jti;
  • type: ['VerifiableCredential'] in place of the DDO's type;
  • an issuer that differs only in the case of an Ethereum address, or any issuer when the DDO declares none;
  • with a vc wrapper, any claims outside vc, which the node does not index.

Anything else that differs is refused. The built-in signers already sign exactly this, and walt.id signs the credential it is sent unchanged.

Before the first transaction, nautilus sizes the signed envelope from the DDO plus 1024 characters for the JWS header and signature, and refuses a DDO that could exceed the node's decrypt limit. A signer that adds more than that is still caught before the envelope is stored, but only after the mint.