Bio-DID-Sequencer
Bio-DID-Seq is the API service that ties the platform together. It keeps accounts and the did:bio records those accounts own, seals files and stores them on IPFS, validates the UCAN passes that share them, plans the registry transactions a wallet signs, and proxies to BioAgents, Dataverse and an AI model. A dataset that already has a DOI keeps it. The DID record carries the DOI and the DID document points at it, so adopting a DID never means abandoning the citation infrastructure a dataset already lives in.
How it is layered
The service splits into five layers. Each layer has a different trust model, and keeping them separate lets one be swapped or audited without touching the others.
Storage
A private IPFS cluster. Several Kubo peers share a swarm key, so they connect only to each other and never to the public network, and IPFS Cluster replicates every pin across them. Every payload is sealed under a fresh content key before it is added, and pinned on every peer as part of the write, so a CID addresses ciphertext bound to the account that uploaded it. Public metadata documents for DIDs are added in the clear and referenced from the DID document. Integrity checking is built into the address, because a tampered block does not match its CID.
Identity
The did:bio method, W3C DID 1.0 conformant, specified in bio-did-spec v1.3 and anchored on Solana through the bio-did-registry program. A researcher's DID is their wallet address and resolves generatively, with no transaction. A dataset or paper is an owned subject, a program derived address the wallet creates, updates and retires through transactions it signs and pays for. The service holds no signing key for anyone. ML-DSA-87 is a first class verification method type in the documents.
Authorization
UCAN 1.0 through pq-ucan. A share is a delegation the owner's wallet signs. A read is an invocation the recipient's browser session key signs. The service is the executor and validates the whole chain before it opens a byte. Capabilities live inside the signed payload, so a holder cannot widen a grant. Revocation is recorded here and checked on every use.
Application
Rust, axum and Postgres. Uploads can run asynchronously behind a task handle, paper ingestion runs in the background, and every request carries an id, a timeout, a body limit and a per session or per address rate budget.
Integration
The connectors outward are BioAgents for the knowledge base, the knowledge graph and protein structure lookups, the Dataverse API, called with the user's own token. The BioAgents agent itself runs on an OpenAI, Anthropic or OpenRouter model.
The DID document and the metadata document
Two documents describe every asset. The DID document lives on the registry and holds keys, controllers and service endpoints, and nothing else. This one is for a dataset, as GET /api/did/resolve/{did} returns it, trimmed of the resolution metadata.
{
"@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://bafkrei..." },
{ "id": "did:bio:devnet:7Qm3...#dataverse", "type": "DataverseRepository",
"serviceEndpoint": "https://doi.org/10.7910/DVN/EXAMPLE" }
]
}The #default key is the owning wallet's, so the wallet can keep changing the document. The metadata document is what the #metadata service points at. It is a public JSON file on IPFS that the API rewrites and repins whenever the record changes.
{
"id": "did:bio:devnet:7Qm3...",
"kind": "dataset",
"doi": "10.7910/DVN/EXAMPLE",
"title": "CRISPR-Cas9 Gene Editing Dataset",
"description": null,
"researchers": [
{ "name": "Jane Smith", "orcid": "0000-0001-2345-6789", "role": "Principal Investigator", "affiliation": null }
],
"keywords": ["CRISPR", "gene editing", "genomics"],
"data_type": null,
"license": "CC-BY-4.0",
"related_identifiers": [],
"funding": [],
"created": "2026-07-24T10:00:00Z",
"updated": "2026-07-24T10:00:00Z"
}Names and ORCIDs appear because the owner chose to publish them. An email never does.
The three workflows that matter
Most interactions with the service reduce to one of these.
Creating and anchoring a DID
The API derives the subject and publishes the metadata. The wallet writes to the chain. The API confirms by reading the chain back.
Registering a paper
A paper is a file you uploaded plus a DID of kind paper. Registration feeds the file to the BioAgents knowledge base in the background and fills in metadata the knowledge graph already holds for its DOI.
Sharing a file
The service prepares bytes, the wallet signs them, and the service records the pass. The recipient redeems it with an invocation their session key signed.
API surface
All routes live under /api and answer JSON. Unless listed as public, they take Authorization: Bearer <session token>. Errors share one envelope, described on Errors & Rate Limits.
| Area | Routes with a session | Public routes |
|---|---|---|
| Health | GET /health, GET /ready | |
| Accounts | POST /signout, GET /users/me, GET /users/search?q= | POST /signup, POST /signin |
| Wallets | GET /wallet, POST /wallet/nonce, POST /wallet/verify, DELETE /wallet/{address}, GET /wallet/identity | POST /wallet/challenge, POST /wallet/signup, POST /wallet/signin |
| Files | POST /upload[?async=true], GET /upload/status/{task_id}, GET /download/{cid}, GET /metadata/{cid}, POST /delete, GET /pins | |
| IPNS | POST /ipns/create, POST /ipns/update?ipns_name=, GET /ipns/by-cid/{cid}, GET /ipns/{name}, GET /ipns/{name}/versions, GET /ipns/{name}/resolve | |
| DIDs | GET /did, POST /did, GET /did/{did}, PUT /did/{did}, POST /did/{did}/dataverse, POST /did/{did}/deactivate (unanchored records only) | GET /did/resolve/{did} |
| Workflows | GET /workflows[?did=], POST /workflows (kind is anchor, sync or deactivate), GET /workflows/{id}, POST /workflows/{id}/confirm | |
| UCAN | POST /ucan/issue/prepare, POST /ucan/issue, POST /ucan/session/prepare, POST /ucan/session, POST /ucan/invoke/prepare, POST /ucan/revoke, GET /ucan/issued, GET /ucan/received, GET /ucan/token/{cid} | POST /ucan/validate |
| Sharing | POST /share/file/prepare, POST /share/file, DELETE /share/revoke/{cid}, GET /shared | |
| Papers | POST /research-paper, GET /research-paper/did/{did}, GET /research-paper/cid/{cid}, GET /research-paper/search?query=, PUT /research-paper/{did}, POST /research-paper/{did}/ingest, GET /research-paper/{did}/knowledge-graph | |
| Dataverse | GET /dataverse/collections, POST /dataverse/dataset, POST /dataverse/dataset/file/{pid}, PUT /dataverse/dataset/metadata, POST /dataverse/dataset/publish, GET /dataverse/dataset/{pid} and its /files, /versions and /citation, GET /dataverse/search, GET /dataverse/file/{id} | |
| BioAgents | POST /bioagents/query, POST /bioagents/query-ws, POST /bioagents/search, POST /bioagents/knowledge, POST /bioagents/knowledge/upload?agent_id=, GET /bioagents/knowledge/search | GET /bioagents/health, GET /bioagents/agents |
| Knowledge graph | GET /bioagents/kg/papers, GET /bioagents/kg/authors, GET /bioagents/kg/paper?doi=, GET /bioagents/kg/hypotheses, POST /bioagents/kg/ask | GET /bioagents/kg/health |
| Protein structure | GET /bioagents/structure/resolve, /report, /variant, /models | GET /bioagents/structure/health |
| AI | POST /ai/chat, POST /ai/process-paper | GET /ai/models |
| Collaboration | GET /ws?token=, a WebSocket carrying subscribe and sync messages between collaborators |
Create a DID
curl -X POST https://api.ekayana.io/api/did \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"kind": "dataset",
"metadata": {
"title": "Research Dataset",
"researchers": [{ "name": "John Doe", "role": "Lead Researcher" }],
"keywords": ["biology", "genomics"],
"license": "CC-BY-4.0"
}
}'The account needs a linked wallet, because the subject is derived from it. Without one the call answers 409. The response is the record with anchored: false. Anchoring is a separate, wallet signed workflow.
GDPR
The architecture provides the following.
- Nothing personal is anchored on-chain. Only public keys, opaque fragments and URIs are. The metadata document carries what the owner chose to publish and never an email address.
- Erasure is effective. Every payload is encrypted before it reaches IPFS, and its content key is wrapped to a key only the service holds. Deleting a file unpins the platform's copy, and a replica a third party fetched earlier is an envelope nobody can open.
- Access is explicit. Every share is a signed, expiring, revocable pass naming one CID and the actions allowed. There is no ambient organisation wide read to audit around, and no API key that grants everything.
- Deactivation is final. Retiring an anchored DID leaves a permanent tombstone on the registry rather than a gap someone could reoccupy.
- A compromise of the service exposes ciphertext and public documents rather than authority. The service signs no passes and no transactions on a user's behalf, so there is no key on the server that could act as you.
The registry is a public program. Anyone with a keypair can write to it, and no single operator's disappearance takes your identifiers with it.