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 envelopeThe 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 issuerWriting 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,iatandjti; type: ['VerifiableCredential']in place of the DDO'stype;- an
issuerthat differs only in the case of an Ethereum address, or any issuer when the DDO declares none; - with a
vcwrapper, any claims outsidevc, 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.