Anatomy of a did:solidus Document: Every Field Explained, and Three Places Where It Is Not the Shape a Standard Library Expects
The document, from the chain
One call returns the whole thing, with no account and no key:
solidus_didResolvewithdid:solidus:testnet:46Hzv2Ek4MXwj1Kenvix4tWCPr1J
What comes back is a single object. The rest of this page is what each part of it is for.
The identity parts
id is the identifier itself, and it is the subject of every other statement in the document.
controller is who may change it. On this document the controller is the subject, which is
the self-sovereign case: nobody else can rewrite it.
verification_method is the list of keys. Each entry has its own identifier ending in a fragment,
a type naming the algorithm, the controller of that key, and the key material itself.
The five relationships, and why there are five
A key is not simply trusted for everything. The document lists, separately, which keys may:
authenticate as the subject assert claims, which is what signs a credential agree keys for encryption invoke a capability delegate one
All five are present and populated on this chain, rather than the common shortcut of listing
authentication and leaving the rest implied. key_agreement comes back as an explicit empty list,
which says "none" rather than "unspecified", and those are different statements.
The parts that are not the document
active, created_ms, updated_ms, version_id, recovery_policy and recovery_nonce sit
alongside the rest.
In the resolution model those are metadata about the document rather than fields of it, and they belong in a separate metadata object. Here they arrive inline.
The practical consequence is small and real: a consumer that hands this object straight to a standard processor is handing it properties that model does not define.
The field names are the node's, not the standard's
The endpoint emits context, verification_method, assertion_method, key_agreement,
capability_invocation and capability_delegation. The standard property names are the
camel-cased forms, and the first one is prefixed.
So what you get from a direct call is not a document a standard resolver library will read. It will find no verification methods, because the property it looks for is not there.
The Solidus client library does the conversion, and does it properly: it emits the prefixed context with two entries, renames every relationship, and turns the timestamps into dates.
Which means conformance lives in the library rather than on the wire. Anybody integrating without that library has to know it, and nothing in the response tells them.
And the key encoding is missing its prefix
The conversion renders the key as a multibase value, which is the standard's preferred form.
It base58-encodes the raw key bytes and prefixes the result with z. A conformant value for
this key type carries a two-byte codec identifier before the key bytes, and that identifier is why
every conformant key of this type begins z6Mk.
Ours does not. Encoding the same key both ways in one command:
ours:
z62rYKgP5rhiNcQGY1b4p6PPr72WErY7MspPAbgwNxN9Rconformant:z6MkjV7auvdXCFCqiu7EhA2ewUwqvbn6GRMiZqJ6RxuPsavo
This is in the published package, confirmed by downloading it from the public registry into an empty directory rather than by reading our own source.
It fails closed, not open. A standard library either rejects the value or reads the first two bytes of a real key as a codec identifier and gets a key that verifies nothing. It does not accept anything it should have refused, and this page is not describing a way to forge a signature.
The cost is exactly the thing a decentralised identifier exists for. A key that only our own code can read is a key that does not travel.
What would change this
All three are small edits with one hard part. The property names and the metadata split are a serialization change on the response, and nothing outside depends on the current spelling except our own library, which already renames them.
The key encoding is the one with a cost, because a value already published under the current encoding will not decode under the new one. The honest path is to emit the prefixed form, accept both while reading, and say when the old form stops being accepted, which is a compatibility window rather than a flag day.
None of that is scheduled, and this page is not going to imply it is. It is filed.
What is genuinely right here
Five relationships, populated rather than implied. An explicit empty list where the answer is
none. A service array, empty here, so the shape is ready for endpoints.
A content-hash version identifier, which is the standard's own idea of a version and is a self-check rather than provenance.
And a recovery policy modelled on-chain, present as a field and null until an owner sets one, so recovery is a property of the record rather than a support process.
Where the standards claim actually stands
The method is registered in the W3C DID Method Registry, which is a listing and is checkable in one request. A listing is not a conformance test, and the three findings above are what a conformance test is for.
What you can check yourself
resolve any identifier, then look at the key names in the response
Count how many of them are the standard's spelling. Then take a publicKeyMultibase from
our library and check whether it begins z6Mk.
Keep reading
- Checking Whether a Credential Issuer Is Trusted: The Check Is Live, and the Answer for Everybody Today Is Not Enrolled
- Why There Is No Light Client Yet: Nobody Could Write One From Outside, Because the Node Does Not Publish What One Consumes
- The Sparse Merkle Tree Behind Solidus State: Four Trees, 256 Levels, Rebuilt From Scratch Every Time and Then Thrown Away
- Anchor Transactions: The Word Names Three Different Operations, and Only One of Them Puts Anything Readable On This Chain

