System Architecture
This page is the map. The other docs each zoom into one component; here the concern is how five of them - the Bio-DID-Seq core, BioAgents, the knowledge graph, IPFS storage, and UCAN authorization - fit together, and which one to look at when something crosses a boundary.
Who does what
A useful rule of thumb: the core owns identity, BioAgents owns meaning, the graph owns relationships, IPFS owns bytes, and UCAN owns permission. When you're debugging, the question "which of those five things is wrong?" usually routes you to the right service.
| Component | Primary Responsibility | Secondary Functions |
|---|---|---|
| Bio-DID-Seq Core | DID Management | Metadata Registry, UCAN Issuance |
| BioAgents | Knowledge Extraction | Entity Recognition, Relationship Detection |
| Knowledge Graph | Semantic Representation | SPARQL Queries, Data Discovery |
| IPFS Storage | Decentralized Data Storage | Content Addressing, Data Integrity |
| UCAN Service | Authorization & Access Control | Capability Delegation, Permission Management |
Three cross-component workflows
Each of these touches at least three of the five components, which is exactly why they're diagrammed here rather than on any single component's page.
Processing a research paper
Answering a knowledge query
Delegating access
Integration details
Route map
// Example API routes showing system integration
const apiRoutes = {
// Bio-DID-Seq Core Routes
"/api/did": "DID document management",
"/api/auth": "Authentication & UCAN issuance",
// BioAgents Integration Routes
"/api/bioagents/process": "Process research papers",
"/api/bioagents/query": "Natural language queries",
// Knowledge Graph Routes
"/api/kg/sparql": "SPARQL query endpoint",
"/api/kg/search": "Semantic search interface",
// Data Integration Routes
"/api/dataverse": "Dataverse integration",
"/api/ipfs": "IPFS storage operations"
};How services talk
Synchronous calls are REST. Long-running work - paper processing, bulk uploads
- goes through an event bus so nothing holds a connection open for minutes.
WebSockets push status updates to clients, and internal service-to-service traffic uses gRPC where the serialization overhead of JSON would actually show up in latency.
Security at the seams
Component boundaries are where authorization bugs live, so the same two mechanisms guard every crossing: session tokens establish who you are, UCANs establish what you may do.
Every service that receives a capability token runs the same check:
async function validateCapability(ucanToken, requiredCapability, resourceId) {
const valid = await ucanService.verify(ucanToken);
if (!valid) return false;
const hasCapability = await ucanService.hasCapability(
ucanToken,
requiredCapability,
resourceId
);
return hasCapability;
}Deployment
Everything ships as Docker containers and scales horizontally. The one deployment-relevant subtlety: because the service's signing identity is derived from a seed rather than stored, adding a replica means setting one environment variable, not running a key distribution ceremony.
Why it's split this way
The honest answer is that a monolith would have been easier to build. The split earns its keep in three places: a researcher can trust the storage layer without trusting the AI layer (they never see each other's credentials); the knowledge graph can be rebuilt from scratch without touching a single byte of stored data; and UCAN delegation means collaboration doesn't route through an admin who grants folder permissions. Modularity here isn't an aesthetic - it's what keeps any single component from becoming the thing everyone has to trust.