did:bio identifiers
A did:bio DID names a researcher, a dataset, a paper, or a piece of research infrastructure. This page covers what the identifier is made of, the two kinds of subject it can name, how the platform creates and anchors one, and how to resolve it back, from the API or from the command line.
1. The identifier is derived
A did:bio identifier is the base58btc encoding of 32 bytes called the subject. It is derived rather than assigned.
Those 32 bytes come in two forms, and the difference matters for everything below.
A key subject is an Ed25519 public key. The DID exists the moment the keypair does, needs no transaction, and resolves to a deterministic generative document until something is written on-chain. Your own identity is one of these. It is your wallet's address.
An owned subject is a program derived address that the registry computes from a wallet and a nonce. It lies off the Ed25519 curve, so no keypair exists for it and there is nothing to lose. The wallet that created it is its first verification method, so one signature names a dataset the wallet owns and pays for. Every asset DID on the platform is one of these.
DIDs that differ only in their network segment are distinct DIDs resolved against distinct registries. No equivalence between them is implied.
2. Your identity is your wallet
The platform holds no signing key for anyone. Your did:bio is did:bio:<network>:<wallet address>, and it appears on your profile once a wallet is linked.
curl https://api.ekayana.io/api/users/me -H "Authorization: Bearer $TOKEN"{ "id": 42, "username": "ada", "email": "ada@lab.example",
"did": "did:bio:devnet:HP3Hj9en...", "wallet": "HP3Hj9en..." }Linking is a Sign In With Solana challenge. POST /api/wallet/nonce returns a message to sign, and POST /api/wallet/verify takes the signature. Until a wallet is linked did is null, and any call that needs an identity answers with a 409 asking for one. The app does all of this behind one button.
3. Create an asset DID
POST /api/did creates the record for a dataset, or, with kind, for any short lowercase word such as paper or claim. The API derives an owned subject from your wallet and a fresh nonce and stores the metadata. Nothing is on-chain yet.
curl -X POST https://api.ekayana.io/api/did \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"kind": "dataset",
"metadata": {
"title": "Coral bleaching survey 2026",
"researchers": [{ "name": "Ada Lovelace", "orcid": "0000-0001-2345-6789", "role": "PI" }],
"keywords": ["coral", "bleaching"],
"license": "CC-BY-4.0"
}
}'The response is the record plus, when the registry has one, the resolved document.
{
"did": "did:bio:devnet:7Qm3...",
"kind": "dataset",
"owner_wallet": "HP3Hj9en...",
"anchored": false,
"deactivated": false,
"metadata": { "...": "..." },
"metadata_cid": "bafy...",
"pending_sync": true
}Metadata is published to IPFS as a public document and never contains an email. Until the DID is anchored, resolving it returns notFound, because an owned subject has no generative document.
4. Anchor it with your wallet
Writing to the registry is a transaction your wallet signs and pays for. The API plans it, the wallet sends it, and the API confirms by reading the chain.
curl -X POST https://api.ekayana.io/api/workflows \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "did": "did:bio:devnet:7Qm3...", "kind": "anchor" }'{
"id": "0d2c...",
"kind": "anchor",
"status": "planned",
"plan": {
"cluster": "devnet",
"program_id": "H1gnV4GjNT3UV7AgGNUCkSaciuVVtM7hKb8JhPV3Xxy6",
"fee_payer": "HP3Hj9en...",
"did_account": "9xLd...",
"instructions": [
{ "program_id": "H1gnV4Gj...", "accounts": [ { "pubkey": "HP3Hj9en...", "is_signer": true, "is_writable": true } ], "data": "<base64>" },
{ "...": "one entry per instruction" }
],
"summary": [
"Create did:bio:devnet:7Qm3... in the registry, controlled by HP3Hj9en...",
"Add service #metadata (BioMetadata) -> ipfs://bafy...",
"Name did:bio:devnet:HP3Hj9en... as controller"
],
"rent_delta_lamports": 2247840,
"fee_lamports": 5000
}
}The plan lists every instruction with its accounts and data, the rent it will lock and the fee. The app builds a transaction from it, the wallet signs and sends, and the client posts the signature.
curl -X POST https://api.ekayana.io/api/workflows/0d2c.../confirm \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "signature": "5Kd9..." }'The API looks the transaction up at confirmed commitment, rereads the account, and only then marks the record anchored. A plan expires if it is not confirmed in time, because the record may have moved on.
Two more workflow kinds cover the rest of the lifecycle. sync computes the difference between the record's services and the document on-chain and plans exactly the adds and removes needed. deactivate plans the permanent tombstone. Both are wallet signed the same way.
5. Resolve it
curl https://api.ekayana.io/api/did/resolve/did:bio:devnet:7Qm3...Resolution is public, as a DID method requires, and always goes to the chain. The API never fabricates lifecycle state. The result is wrapped in resolution metadata.
{
"@context": "https://w3id.org/did-resolution/v1",
"didDocument": {
"@context": ["https://www.w3.org/ns/did/v1", "https://www.w3.org/ns/cid/v1"],
"id": "did:bio:devnet:7Qm3...",
"verificationMethod": [{
"id": "did:bio:devnet:7Qm3...#default",
"type": "Multikey",
"controller": "did:bio:devnet:7Qm3...",
"publicKeyMultibase": "z6Mk..."
}],
"authentication": ["did:bio:devnet:7Qm3...#default"],
"assertionMethod": ["did:bio:devnet:7Qm3...#default"],
"capabilityInvocation": ["did:bio:devnet:7Qm3...#default"],
"controller": ["did:bio:devnet:HP3Hj9en..."],
"service": [{
"id": "did:bio:devnet:7Qm3...#metadata",
"type": "BioMetadata",
"serviceEndpoint": "ipfs://bafy..."
}]
},
"didResolutionMetadata": { "contentType": "application/did+ld+json" },
"didDocumentMetadata": { "versionId": "3", "updated": "2026-09-16T10:41:07Z", "deactivated": false }
}versionId is the registry's monotonic counter, so two reads that disagree tell you which one is stale. The #default method is the wallet's key, so the wallet can keep changing the document.
| Operation | Endpoint |
|---|---|
| Full resolution result | GET /api/did/resolve/{did} (public) |
| Record with document | GET /api/did/{did} |
| List your DIDs | GET /api/did |
| Update metadata | PUT /api/did/{did} |
| Link a Dataverse DOI | POST /api/did/{did}/dataverse |
| Plan a chain change | POST /api/workflows with anchor, sync or deactivate |
6. From the command line
The same registry is open to anyone with a keypair, and no account is needed. bio-did-resolver resolves any DID and drives every instruction except update_service.
cargo install bio-did-resolver
# Any DID, any cluster. The network segment picks the RPC endpoint.
bio-did-resolver resolve did:bio:devnet:2T6zLFvMx7NJac5qQtiKTaPhMwHLkwKETWjUK1yKv4tc
# Your keypair's own DID. Register it and publish a metadata service.
bio-did-resolver init
bio-did-resolver add-service metadata BioMetadata ipfs://<cid>
# An asset the keypair owns. The same nonce always names the same DID.
bio-did-resolver init-owned 42
bio-did-resolver add-service metadata BioMetadata ipfs://<cid> did:bio:devnet:<OWNED>
# A post-quantum assertion key, uploaded through a key buffer in five transactions.
bio-did-resolver add-key pq --type ml-dsa-87 --key-file pq.pub --flags assertion--dry-run simulates and prints the program logs instead of sending. Requests the program would refuse fail before they leave the machine, with a reason.
Which DID goes where
- Your wallet's DID is who you are. It is the audience of every share made to you and the issuer of every share you make.
- An asset's DID is what a share is about. It carries the metadata service, names your wallet's DID as controller, and can be linked to a DOI without giving up either identifier.
- Naming your wallet's DID as controller grants authority on-chain. Once your wallet's DID has a registry entry, a key you add to it can update every asset that names it, so a new device key reaches all of your assets with one change.
The sharing tutorial grants a capability over a file to a wallet DID. Post Quantum Security covers attaching an ML-DSA-87 key to either kind of document.