Protocol Labs Ecosystem Integration
Most of the decentralized storage stack this platform stands on comes from one place: Protocol Labs and the ecosystem around it. Rather than reinvent content addressing, peer-to-peer networking, or capability tokens, Bio-DID-Seq composes the pieces that already exist, are specified, and have survived a decade of production use. This page is the inventory - what each piece is, and what it does here:
| Technology | Purpose in Bio-DID-Seq |
|---|---|
| IPFS | Content addressed storage for research data |
| Filecoin | Long term verifiable storage with cryptographic proofs |
| libp2p | Peer-to-peer networking for decentralized communication |
| IPLD | Interoperable data model for linked data structures |
| UCAN | User controlled authorization for capability based security |
| Storacha | Hot storage layer for fast retrieval |
| Saturn CDN | Global content delivery network for IPFS |
| FVM | Smart contracts for programmable storage deals |
| drand | Distributed randomness for cryptographic operations |
IPFS
IPFS is the load-bearing one - everything else on this page is optional, IPFS isn't. Every piece of research data is stored content-addressed:
interface IPFSContent {
cid: string; // Content Identifier (cryptographic hash)
size: number; // File size in bytes
links: IPFSLink[]; // DAG links to other content
metadata: {
did: string; // Associated DID
timestamp: number; // Upload timestamp
version: number; // Content version
};
}
async function uploadResearchData(file: Blob, token: string) {
const body = new FormData();
body.append('file', file);
const res = await fetch('https://api.ekayana.com/api/upload', {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
body,
});
const result = await res.json();
return {
cid: result.cid,
gatewayUrl: `https://gateway.ekayana.com/ipfs/${result.cid}`,
};
}Uploads are pinned as part of the write, so no separate pin step is required.
Filecoin
Pinning keeps data available; Filecoin makes that availability provable. A storage deal obliges a provider to keep submitting cryptographic proofs (PoRep at sealing time, PoSt continuously) that the exact bytes are still held - so "the archive is intact" becomes something you verify, not something you take on faith:
interface FilecoinDeal {
dealId: string;
cid: string;
provider: string;
startEpoch: number;
endEpoch: number;
verified: boolean;
proofType: 'PoRep' | 'PoSt';
}
async function createStorageDeal(cid: string, duration: number) {
const deal = await filecoinClient.createDeal({
cid,
duration: duration,
replication: 3,
verified: true,
fastRetrieval: true
});
return deal;
}The Filecoin Virtual Machine extends this with programmable storage, deals managed by smart contracts, data DAOs governing who funds and who accesses a shared archive. That's the direction long term research archiving is heading.
libp2p
libp2p is the networking layer underneath IPFS, and Bio-DID-Seq uses it directly for node to node communication, encrypted transports, a Kademlia DHT for peer discovery, gossipsub for update propagation:
import { createLibp2p } from 'libp2p';
import { noise } from '@chainsafe/libp2p-noise';
import { kadDHT } from '@libp2p/kad-dht';
import { gossipsub } from '@chainsafe/libp2p-gossipsub';
const node = await createLibp2p({
transports: [tcp(), webSockets()],
connectionEncryption: [noise()],
services: {
dht: kadDHT({ clientMode: false }),
pubsub: gossipsub({ allowPublishToZeroTopicPeers: true })
}
});
// Subscribe to research data updates
await node.services.pubsub.subscribe('bio-did-seq/updates');IPLD
IPLD is what makes CIDs more than file handles: any structure can hold links to other content addressed structures, forming a DAG. A research paper here is exactly that, a node whose edges point at author DIDs, the full content, the papers it cites, and its extracted knowledge graph, each independently addressable and verifiable:
interface ResearchPaperDAG {
'@context': string[];
did: string;
title: string;
authors: CID[]; // Links to author DIDs
content: CID; // Link to full content
references: CID[]; // Links to referenced papers
knowledgeGraph: CID; // Link to extracted knowledge
}Storacha (web3.storage)
Filecoin deals are cheap and durable but not fast to read from. Storacha fills the gap as the hot layer - UCAN-native, which fits this platform unusually well, since the same capability model governs both storage and authorization:
import { create } from '@web3-storage/w3up-client';
const client = await create();
async function uploadToStoracha(file: File, metadata: ResearchMetadata) {
const space = await client.createSpace('research-project');
await client.setCurrentSpace(space.did());
const cid = await client.uploadFile(file);
return {
dataCid: cid.toString(),
space: space.did()
};
}Saturn CDN
Saturn is a CDN with one property ordinary CDNs can't offer: because content is addressed by hash, the client can verify every byte it receives against the CID it asked for. A malicious or compromised edge node can refuse to serve you, but it cannot serve you the wrong thing undetected:
async function retrieveViaSaturn(cid: string) {
const response = await fetch(`https://strn.pl/ipfs/${cid}`);
const data = await response.arrayBuffer();
// Verify content hash matches CID
const verified = await verifyCID(cid, new Uint8Array(data));
return { data, verified };
}drand
drand is a distributed randomness beacon - a value nobody could predict and nobody could bias, published on a fixed schedule with a proof. The research use case is fairness you can audit: select reviewers with drand and anyone can verify the selection wasn't steered, without trusting the selector:
import { HttpChainClient, fetchBeacon } from 'drand-client';
async function getVerifiableRandomness() {
const chain = new HttpChainClient('https://api.drand.sh');
const beacon = await fetchBeacon(chain);
return {
round: beacon.round,
randomness: beacon.randomness,
signature: beacon.signature
};
}
// Use for fair reviewer selection
async function selectReviewers(candidates: string[], numReviewers: number) {
const beacon = await getVerifiableRandomness();
const shuffled = deterministicShuffle(candidates, beacon.randomness);
return shuffled.slice(0, numReviewers);
}